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