create changelog entry
[debian/openrocket] / android / src / net / sf / openrocket / android / util / ExpandableListFragment.java
1 package net.sf.openrocket.android.util;\r
2 \r
3 import android.os.Bundle;\r
4 import android.os.Handler;\r
5 import android.view.ContextMenu;\r
6 import android.view.ContextMenu.ContextMenuInfo;\r
7 import android.view.Gravity;\r
8 import android.view.LayoutInflater;\r
9 import android.view.View;\r
10 import android.view.View.OnCreateContextMenuListener;\r
11 import android.view.ViewGroup;\r
12 import android.view.animation.AnimationUtils;\r
13 import android.widget.AdapterView;\r
14 import android.widget.ExpandableListAdapter;\r
15 import android.widget.ExpandableListView;\r
16 import android.widget.FrameLayout;\r
17 import android.widget.ListAdapter;\r
18 import android.widget.ListView;\r
19 import android.widget.TextView;\r
20 \r
21 import com.actionbarsherlock.app.SherlockFragment;\r
22 \r
23 /**\r
24  * \r
25  * Pulled from https://gist.github.com/1316903\r
26  * \r
27  * This class has originally been taken from\r
28  * http://stackoverflow.com/questions/6051050/expandablelistfragment-with-loadermanager-for-compatibility-package\r
29  * and then modified by Manfred Moser <manfred@simpligility.com> to get it to work with the v4 r4 compatibility\r
30  * library. With inspirations from the library source.\r
31  *\r
32  * All ASLv2 licensed.\r
33  */\r
34 public class ExpandableListFragment extends SherlockFragment\r
35 implements OnCreateContextMenuListener, ExpandableListView.OnChildClickListener,\r
36 ExpandableListView.OnGroupCollapseListener, ExpandableListView.OnGroupExpandListener\r
37 {\r
38 \r
39         static final int INTERNAL_EMPTY_ID = 0x00ff0001;\r
40 \r
41         final private Handler mHandler = new Handler();\r
42 \r
43         final private Runnable mRequestFocus = new Runnable() {\r
44                 public void run() {\r
45                         mList.focusableViewAvailable(mList);\r
46                 }\r
47         };\r
48 \r
49         final private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {\r
50                 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {\r
51                         onListItemClick((ListView) parent, v, position, id);\r
52                 }\r
53         };\r
54         \r
55         final private AdapterView.OnItemLongClickListener mOnLongClickListener = new AdapterView.OnItemLongClickListener() {\r
56                 @Override\r
57                 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {\r
58                         return onListItemLongClick( (ListView) parent, view, position, id);\r
59                 }\r
60                 \r
61         };\r
62 \r
63         ExpandableListAdapter mAdapter;\r
64         ExpandableListView mList;\r
65         View mEmptyView;\r
66         TextView mStandardEmptyView;\r
67         View mListContainer;\r
68         boolean mSetEmptyText;\r
69         boolean mListShown;\r
70         boolean mFinishedStart = false;\r
71 \r
72         public ExpandableListFragment() {\r
73         }\r
74 \r
75         /**\r
76          * Provide default implementation to return a simple list view. Subclasses\r
77          * can override to replace with their own layout. If doing so, the\r
78          * returned view hierarchy <em>must</em> have a ListView whose id\r
79          * is {@link android.R.id#list android.R.id.list} and can optionally\r
80          * have a sibling view id {@link android.R.id#empty android.R.id.empty}\r
81          * that is to be shown when the list is empty.\r
82          * <p/>\r
83          * <p>If you are overriding this method with your own custom content,\r
84          * consider including the standard layout {@link android.R.layout#list_content}\r
85          * in your layout file, so that you continue to retain all of the standard\r
86          * behavior of ListFragment. In particular, this is currently the only\r
87          * way to have the built-in indeterminant progress state be shown.\r
88          */\r
89         @Override\r
90         public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\r
91                 FrameLayout root = new FrameLayout(getActivity());\r
92 \r
93                 TextView tv = new TextView(getActivity());\r
94                 tv.setId(INTERNAL_EMPTY_ID);\r
95                 tv.setGravity(Gravity.CENTER);\r
96                 root.addView(tv,\r
97                                 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));\r
98 \r
99                 ExpandableListView lv = new ExpandableListView(getActivity());\r
100                 lv.setId(android.R.id.list);\r
101                 lv.setDrawSelectorOnTop(false);\r
102                 lv.setOnChildClickListener(this);\r
103                 lv.setOnGroupExpandListener(this);\r
104                 lv.setOnGroupCollapseListener(this);\r
105 \r
106                 root.addView(lv, new FrameLayout.LayoutParams(\r
107                                 ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));\r
108 \r
109                 ListView.LayoutParams lp =\r
110                                 new ListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT);\r
111                 root.setLayoutParams(lp);\r
112 \r
113                 return root;\r
114         }\r
115 \r
116         /**\r
117          * Attach to list view once the view hierarchy has been created.\r
118          */\r
119         @Override\r
120         public void onViewCreated(View view, Bundle savedInstanceState) {\r
121                 super.onViewCreated(view, savedInstanceState);\r
122                 ensureList();\r
123         }\r
124 \r
125         /** Detach from list view. */\r
126         @Override\r
127         public void onDestroyView() {\r
128                 mHandler.removeCallbacks(mRequestFocus);\r
129                 mList = null;\r
130                 super.onDestroyView();\r
131         }\r
132 \r
133         /**\r
134          * This method will be called when an item in the list is selected.\r
135          * Subclasses should override. Subclasses can call\r
136          * getListView().getItemAtPosition(position) if they need to access the\r
137          * data associated with the selected item.\r
138          * @param l The ListView where the click happened\r
139          * @param v The view that was clicked within the ListView\r
140          * @param position The position of the view in the list\r
141          * @param id The row id of the item that was clicked\r
142          */\r
143         public void onListItemClick(ListView l, View v, int position, long id) {\r
144         }\r
145 \r
146         public boolean onListItemLongClick(ListView l, View v, int position, long id ) {\r
147                 return false;\r
148         }\r
149         \r
150         /** Provide the cursor for the list view. */\r
151         public void setListAdapter(ExpandableListAdapter adapter) {\r
152                 boolean hadAdapter = mAdapter != null;\r
153                 mAdapter = adapter;\r
154                 if (mList != null) {\r
155                         mList.setAdapter((ExpandableListAdapter) null);\r
156                         mList.setAdapter(adapter);\r
157                         if (!mListShown && !hadAdapter) {\r
158                                 // The list was hidden, and previously didn't have an\r
159                                 // adapter. It is now time to show it.\r
160                                 setListShown(true, getView().getWindowToken() != null);\r
161                         }\r
162                 }\r
163         }\r
164 \r
165         /**\r
166          * Set the currently selected list item to the specified\r
167          * position with the adapter's data\r
168          */\r
169         public void setSelection(int position) {\r
170                 ensureList();\r
171                 mList.setSelection(position);\r
172         }\r
173 \r
174         public long getSelectedPosition() {\r
175                 ensureList();\r
176                 return mList.getSelectedPosition();\r
177         }\r
178 \r
179         public long getSelectedId() {\r
180                 ensureList();\r
181                 return mList.getSelectedId();\r
182         }\r
183 \r
184         public ExpandableListView getExpandableListView() {\r
185                 ensureList();\r
186                 return mList;\r
187         }\r
188 \r
189         /**\r
190          * The default content for a ListFragment has a TextView that can\r
191          * be shown when the list is empty. If you would like to have it\r
192          * shown, call this method to supply the text it should use.\r
193          */\r
194         public void setEmptyText(CharSequence text) {\r
195                 ensureList();\r
196                 if (mStandardEmptyView == null) {\r
197                         throw new IllegalStateException("Can't be used with a custom content view");\r
198                 }\r
199                 mStandardEmptyView.setText(text);\r
200                 if (!mSetEmptyText) {\r
201                         mList.setEmptyView(mStandardEmptyView);\r
202                         mSetEmptyText = true;\r
203                 }\r
204         }\r
205 \r
206         /**\r
207          * Control whether the list is being displayed. You can make it not\r
208          * displayed if you are waiting for the initial data to show in it. During\r
209          * this time an indeterminant progress indicator will be shown instead.\r
210          * <p/>\r
211          * <p>Applications do not normally need to use this themselves. The default\r
212          * behavior of ListFragment is to start with the list not being shown, only\r
213          * showing it once an adapter is given with {@link #setListAdapter(ListAdapter)}.\r
214          * If the list at that point had not been shown, when it does get shown\r
215          * it will be do without the user ever seeing the hidden state.\r
216          * @param shown If true, the list view is shown; if false, the progress\r
217          * indicator. The initial value is true.\r
218          */\r
219         public void setListShown(boolean shown) {\r
220                 setListShown(shown, true);\r
221         }\r
222 \r
223         /**\r
224          * Like {@link #setListShown(boolean)}, but no animation is used when\r
225          * transitioning from the previous state.\r
226          */\r
227         public void setListShownNoAnimation(boolean shown) {\r
228                 setListShown(shown, false);\r
229         }\r
230 \r
231         /**\r
232          * Control whether the list is being displayed. You can make it not\r
233          * displayed if you are waiting for the initial data to show in it. During\r
234          * this time an indeterminant progress indicator will be shown instead.\r
235          * @param shown If true, the list view is shown; if false, the progress\r
236          * indicator. The initial value is true.\r
237          * @param animate If true, an animation will be used to transition to the\r
238          * new state.\r
239          */\r
240         private void setListShown(boolean shown, boolean animate) {\r
241                 ensureList();\r
242                 if (mListShown == shown) {\r
243                         return;\r
244                 }\r
245                 mListShown = shown;\r
246                 if (mListContainer != null) {\r
247                         if (shown) {\r
248                                 if (animate) {\r
249                                         mListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in));\r
250                                 }\r
251                                 mListContainer.setVisibility(View.VISIBLE);\r
252                         } else {\r
253                                 if (animate) {\r
254                                         mListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out));\r
255                                 }\r
256                                 mListContainer.setVisibility(View.GONE);\r
257                         }\r
258                 }\r
259         }\r
260 \r
261         /** Get the ListAdapter associated with this activity's ListView. */\r
262         public ExpandableListAdapter getExpandableListAdapter() {\r
263                 return mAdapter;\r
264         }\r
265 \r
266         private void ensureList() {\r
267                 if (mList != null) {\r
268                         return;\r
269                 }\r
270                 View root = getView();\r
271                 if (root == null) {\r
272                         throw new IllegalStateException("Content view not yet created");\r
273                 }\r
274                 if (root instanceof ExpandableListView) {\r
275                         mList = (ExpandableListView) root;\r
276                 } else {\r
277                         mStandardEmptyView = (TextView) root.findViewById(INTERNAL_EMPTY_ID);\r
278                         if (mStandardEmptyView == null) {\r
279                                 mEmptyView = root.findViewById(android.R.id.empty);\r
280                         }\r
281                         mListContainer = root.findViewById(android.R.id.list);\r
282                         View rawListView = root.findViewById(android.R.id.list);\r
283                         if (!(rawListView instanceof ExpandableListView)) {\r
284                                 if (rawListView == null) {\r
285                                         throw new RuntimeException("Your content must have a ExpandableListView whose id attribute is " +\r
286                                                         "'android.R.id.list'");\r
287                                 }\r
288                                 throw new RuntimeException("Content has view with id attribute 'android.R.id.list' " +\r
289                                                 "that is not a ExpandableListView class");\r
290                         }\r
291                         mList = (ExpandableListView) rawListView;\r
292                         if (mEmptyView != null) {\r
293                                 mList.setEmptyView(mEmptyView);\r
294                         }\r
295                 }\r
296                 mListShown = true;\r
297                 mList.setOnItemClickListener(mOnClickListener);\r
298                 mList.setOnItemLongClickListener(mOnLongClickListener);\r
299                 if (mAdapter != null) {\r
300                         setListAdapter(mAdapter);\r
301                 } else {\r
302                         // We are starting without an adapter, so assume we won't\r
303                         // have our data right away and start with the progress indicator.\r
304                         setListShown(false, false);\r
305                 }\r
306                 mHandler.post(mRequestFocus);\r
307         }\r
308 \r
309         @Override\r
310         public void onGroupExpand(int arg0) {\r
311                 // TODO Auto-generated method stub\r
312 \r
313         }\r
314 \r
315         @Override\r
316         public void onGroupCollapse(int arg0) {\r
317                 // TODO Auto-generated method stub\r
318 \r
319         }\r
320 \r
321         @Override\r
322         public boolean onChildClick(ExpandableListView arg0, View arg1, int arg2, int arg3, long arg4) {\r
323                 // TODO Auto-generated method stub\r
324                 return false;\r
325         }\r
326 \r
327         @Override\r
328         public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {\r
329         }\r
330 \r
331         public void onContentChanged() {\r
332                 View emptyView = getView().findViewById(android.R.id.empty);\r
333                 mList = (ExpandableListView) getView().findViewById(android.R.id.list);\r
334                 if (mList == null) {\r
335                         throw new RuntimeException(\r
336                                         "Your content must have a ExpandableListView whose id attribute is " + "'android.R.id.list'");\r
337                 }\r
338                 if (emptyView != null) {\r
339                         mList.setEmptyView(emptyView);\r
340                 }\r
341                 mList.setOnChildClickListener(this);\r
342                 mList.setOnGroupExpandListener(this);\r
343                 mList.setOnGroupCollapseListener(this);\r
344 \r
345                 if (mFinishedStart) {\r
346                         setListAdapter(mAdapter);\r
347                 }\r
348                 mFinishedStart = true;\r
349         }\r
350 }\r
351 \r