Merge branch 'develop' of github.com:owncloud/android into switchListVsGrid
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / fragment / ExtendedListFragment.java
1 /**
2 * ownCloud Android client application
3 *
4 * Copyright (C) 2012 Bartek Przybylski
5 * Copyright (C) 2012-2015 ownCloud Inc.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2,
9 * as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 package com.owncloud.android.ui.fragment;
22
23 import java.util.ArrayList;
24
25 import android.os.Build;
26 import android.os.Bundle;
27 import android.support.v4.app.Fragment;
28 import android.support.v4.widget.SwipeRefreshLayout;
29 import android.view.LayoutInflater;
30 import android.view.View;
31 import android.view.ViewGroup;
32 import android.widget.AbsListView;
33 import android.widget.AdapterView;
34 import android.widget.AdapterView.OnItemClickListener;
35 import android.widget.GridView;
36 import android.widget.ListAdapter;
37 import android.widget.ListView;
38 import android.widget.TextView;
39
40 import com.owncloud.android.R;
41 import com.owncloud.android.lib.common.utils.Log_OC;
42 import com.owncloud.android.ui.ExtendedListView;
43 import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
44 import com.owncloud.android.ui.adapter.FileListListAdapter;
45
46 import third_parties.in.srain.cube.GridViewWithHeaderAndFooter;
47
48 /**
49 * TODO extending SherlockListFragment instead of SherlockFragment
50 */
51 public class ExtendedListFragment extends Fragment
52 implements OnItemClickListener, OnEnforceableRefreshListener {
53
54 private static final String TAG = ExtendedListFragment.class.getSimpleName();
55
56 private static final String KEY_SAVED_LIST_POSITION = "SAVED_LIST_POSITION";
57 private static final String KEY_INDEXES = "INDEXES";
58 private static final String KEY_FIRST_POSITIONS= "FIRST_POSITIONS";
59 private static final String KEY_TOPS = "TOPS";
60 private static final String KEY_HEIGHT_CELL = "HEIGHT_CELL";
61 private static final String KEY_EMPTY_LIST_MESSAGE = "EMPTY_LIST_MESSAGE";
62
63 private SwipeRefreshLayout mRefreshListLayout;
64 private SwipeRefreshLayout mRefreshGridLayout;
65 private SwipeRefreshLayout mRefreshEmptyLayout;
66 private TextView mEmptyListMessage;
67
68 // Save the state of the scroll in browsing
69 private ArrayList<Integer> mIndexes;
70 private ArrayList<Integer> mFirstPositions;
71 private ArrayList<Integer> mTops;
72 private int mHeightCell = 0;
73
74 private OnEnforceableRefreshListener mOnRefreshListener = null;
75
76 protected AbsListView mCurrentListView;
77 private ExtendedListView mListView;
78 private View mListFooterView;
79 private GridViewWithHeaderAndFooter mGridView;
80 private View mGridFooterView;
81
82 private ListAdapter mAdapter;
83
84 protected void setListAdapter(ListAdapter listAdapter) {
85 mAdapter = listAdapter;
86 if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
87 mCurrentListView.setAdapter(listAdapter);
88 } else {
89 ((ListView)mCurrentListView).setAdapter(listAdapter);
90 }
91
92 mCurrentListView.invalidate();
93 }
94
95 protected AbsListView getListView() {
96 return mCurrentListView;
97 }
98
99
100 public void switchToGridView() {
101 if ((mCurrentListView == mListView)) {
102
103 mListView.setAdapter(null);
104 mRefreshListLayout.setVisibility(View.GONE);
105
106 if (mAdapter instanceof FileListListAdapter) {
107 ((FileListListAdapter) mAdapter).setGridMode(true);
108 }
109 mGridView.setAdapter(mAdapter);
110 mRefreshGridLayout.setVisibility(View.VISIBLE);
111
112 mCurrentListView = mGridView;
113 }
114 }
115
116 public void switchToListView() {
117 if (mCurrentListView == mGridView) {
118 mGridView.setAdapter(null);
119 mRefreshGridLayout.setVisibility(View.GONE);
120
121 if (mAdapter instanceof FileListListAdapter) {
122 ((FileListListAdapter) mAdapter).setGridMode(false);
123 }
124 mListView.setAdapter(mAdapter);
125 mRefreshListLayout.setVisibility(View.VISIBLE);
126
127 mCurrentListView = mListView;
128 }
129 }
130
131 public boolean isGridView(){
132 if (mAdapter instanceof FileListListAdapter) {
133 return ((FileListListAdapter) mAdapter).isGridMode();
134 }
135 return false;
136 }
137
138
139 @Override
140 public View onCreateView(LayoutInflater inflater, ViewGroup container,
141 Bundle savedInstanceState) {
142 Log_OC.d(TAG, "onCreateView");
143
144 View v = inflater.inflate(R.layout.list_fragment, null);
145
146 mListView = (ExtendedListView)(v.findViewById(R.id.list_root));
147 mListView.setOnItemClickListener(this);
148 mListFooterView = inflater.inflate(R.layout.list_footer, null, false);
149
150 mGridView = (GridViewWithHeaderAndFooter) (v.findViewById(R.id.grid_root));
151 mGridView.setNumColumns(GridView.AUTO_FIT);
152 mGridView.setOnItemClickListener(this);
153 mGridFooterView = inflater.inflate(R.layout.list_footer, null, false);
154
155 if (savedInstanceState != null) {
156 int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
157 if (mCurrentListView == mListView) {
158 Log_OC.v(TAG, "Setting and centering around list position " + referencePosition);
159 mListView.setAndCenterSelection(referencePosition);
160 } else {
161 Log_OC.v(TAG, "Setting grid position " + referencePosition);
162 mGridView.setSelection(referencePosition);
163 }
164 }
165
166 // Pull-down to refresh layout
167 mRefreshListLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_list);
168 mRefreshGridLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_grid);
169 mRefreshEmptyLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_empty);
170 mEmptyListMessage = (TextView) v.findViewById(R.id.empty_list_view);
171
172 onCreateSwipeToRefresh(mRefreshListLayout);
173 onCreateSwipeToRefresh(mRefreshGridLayout);
174 onCreateSwipeToRefresh(mRefreshEmptyLayout);
175
176 mListView.setEmptyView(mRefreshEmptyLayout);
177 mGridView.setEmptyView(mRefreshEmptyLayout);
178
179 mCurrentListView = mListView; // list as default
180
181 return v;
182 }
183
184 /**
185 * {@inheritDoc}
186 */
187 @Override
188 public void onActivityCreated(Bundle savedInstanceState) {
189 super.onActivityCreated(savedInstanceState);
190
191 if (savedInstanceState != null) {
192 mIndexes = savedInstanceState.getIntegerArrayList(KEY_INDEXES);
193 mFirstPositions = savedInstanceState.getIntegerArrayList(KEY_FIRST_POSITIONS);
194 mTops = savedInstanceState.getIntegerArrayList(KEY_TOPS);
195 mHeightCell = savedInstanceState.getInt(KEY_HEIGHT_CELL);
196 setMessageForEmptyList(savedInstanceState.getString(KEY_EMPTY_LIST_MESSAGE));
197
198 } else {
199 mIndexes = new ArrayList<Integer>();
200 mFirstPositions = new ArrayList<Integer>();
201 mTops = new ArrayList<Integer>();
202 mHeightCell = 0;
203 }
204 }
205
206
207 @Override
208 public void onSaveInstanceState(Bundle savedInstanceState) {
209 super.onSaveInstanceState(savedInstanceState);
210 Log_OC.d(TAG, "onSaveInstanceState()");
211 savedInstanceState.putInt(KEY_SAVED_LIST_POSITION, getReferencePosition());
212 savedInstanceState.putIntegerArrayList(KEY_INDEXES, mIndexes);
213 savedInstanceState.putIntegerArrayList(KEY_FIRST_POSITIONS, mFirstPositions);
214 savedInstanceState.putIntegerArrayList(KEY_TOPS, mTops);
215 savedInstanceState.putInt(KEY_HEIGHT_CELL, mHeightCell);
216 savedInstanceState.putString(KEY_EMPTY_LIST_MESSAGE, getEmptyViewText());
217 }
218
219 /**
220 * Calculates the position of the item that will be used as a reference to
221 * reposition the visible items in the list when the device is turned to
222 * other position.
223 *
224 * The current policy is take as a reference the visible item in the center
225 * of the screen.
226 *
227 * @return The position in the list of the visible item in the center of the
228 * screen.
229 */
230 protected int getReferencePosition() {
231 if (mCurrentListView != null) {
232 return (mCurrentListView.getFirstVisiblePosition() +
233 mCurrentListView.getLastVisiblePosition()) / 2;
234 } else {
235 return 0;
236 }
237 }
238
239
240 /*
241 * Restore index and position
242 */
243 protected void restoreIndexAndTopPosition() {
244 if (mIndexes.size() > 0) {
245 // needs to be checked; not every browse-up had a browse-down before
246
247 int index = mIndexes.remove(mIndexes.size() - 1);
248 final int firstPosition = mFirstPositions.remove(mFirstPositions.size() -1);
249 int top = mTops.remove(mTops.size() - 1);
250
251 Log_OC.v(TAG, "Setting selection to position: " + firstPosition + "; top: "
252 + top + "; index: " + index);
253
254 if (mCurrentListView == mListView) {
255 if (mHeightCell*index <= mListView.getHeight()) {
256 mListView.setSelectionFromTop(firstPosition, top);
257 } else {
258 mListView.setSelectionFromTop(index, 0);
259 }
260
261 } else {
262 if (mHeightCell*index <= mGridView.getHeight()) {
263 mGridView.setSelection(firstPosition);
264 //mGridView.smoothScrollToPosition(firstPosition);
265 } else {
266 mGridView.setSelection(index);
267 //mGridView.smoothScrollToPosition(index);
268 }
269 }
270
271 }
272 }
273
274 /*
275 * Save index and top position
276 */
277 protected void saveIndexAndTopPosition(int index) {
278
279 mIndexes.add(index);
280
281 int firstPosition = mCurrentListView.getFirstVisiblePosition();
282 mFirstPositions.add(firstPosition);
283
284 View view = mCurrentListView.getChildAt(0);
285 int top = (view == null) ? 0 : view.getTop() ;
286
287 mTops.add(top);
288
289 // Save the height of a cell
290 mHeightCell = (view == null || mHeightCell != 0) ? mHeightCell : view.getHeight();
291 }
292
293
294 @Override
295 public void onItemClick (AdapterView<?> parent, View view, int position, long id) {
296 // to be @overriden
297 }
298
299 @Override
300 public void onRefresh() {
301 mRefreshListLayout.setRefreshing(false);
302 mRefreshGridLayout.setRefreshing(false);
303 mRefreshEmptyLayout.setRefreshing(false);
304
305 if (mOnRefreshListener != null) {
306 mOnRefreshListener.onRefresh();
307 }
308 }
309 public void setOnRefreshListener(OnEnforceableRefreshListener listener) {
310 mOnRefreshListener = listener;
311 }
312
313
314 /**
315 * Disables swipe gesture.
316 *
317 * Sets the 'enabled' state of the refresh layouts contained in the fragment.
318 *
319 * When 'false' is set, prevents user gestures but keeps the option to refresh programatically,
320 *
321 * @param enabled Desired state for capturing swipe gesture.
322 */
323 public void setSwipeEnabled(boolean enabled) {
324 mRefreshListLayout.setEnabled(enabled);
325 mRefreshGridLayout.setEnabled(enabled);
326 mRefreshEmptyLayout.setEnabled(enabled);
327 }
328
329 /**
330 * Set message for empty list view
331 */
332 public void setMessageForEmptyList(String message) {
333 if (mEmptyListMessage != null) {
334 mEmptyListMessage.setText(message);
335 }
336 }
337
338 /**
339 * Get the text of EmptyListMessage TextView
340 *
341 * @return String
342 */
343 public String getEmptyViewText() {
344 return (mEmptyListMessage != null) ? mEmptyListMessage.getText().toString() : "";
345 }
346
347 private void onCreateSwipeToRefresh(SwipeRefreshLayout refreshLayout) {
348 // Colors in animations: background
349 refreshLayout.setColorScheme(R.color.background_color, R.color.background_color,
350 R.color.background_color, R.color.background_color);
351
352 refreshLayout.setOnRefreshListener(this);
353 }
354
355 @Override
356 public void onRefresh(boolean ignoreETag) {
357 mRefreshListLayout.setRefreshing(false);
358 mRefreshGridLayout.setRefreshing(false);
359 mRefreshEmptyLayout.setRefreshing(false);
360
361 if (mOnRefreshListener != null) {
362 mOnRefreshListener.onRefresh(ignoreETag);
363 }
364 }
365
366 protected void setChoiceMode(int choiceMode) {
367 if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
368 mListView.setChoiceMode(choiceMode);
369 mGridView.setChoiceMode(choiceMode);
370 } else {
371 ((ListView)mListView).setChoiceMode(choiceMode);
372 }
373 }
374
375 protected void registerForContextMenu() {
376 registerForContextMenu(mListView);
377 registerForContextMenu(mGridView);
378 mListView.setOnCreateContextMenuListener(this);
379 mGridView.setOnCreateContextMenuListener(this);
380 }
381
382 /**
383 * TODO doc
384 * To be called before setAdapter, or GridViewWithHeaderAndFooter will throw an exception
385 *
386 * @param enabled
387 */
388 protected void setFooterEnabled(boolean enabled) {
389 if (enabled) {
390 if (mGridView.getFooterViewCount() == 0) {
391 if (mGridFooterView.getParent() != null ) {
392 ((ViewGroup) mGridFooterView.getParent()).removeView(mGridFooterView);
393 }
394 mGridView.addFooterView(mGridFooterView, null, false);
395 }
396 mGridFooterView.invalidate();
397
398 if (mListView.getFooterViewsCount() == 0) {
399 if (mListFooterView.getParent() != null ) {
400 ((ViewGroup) mListFooterView.getParent()).removeView(mListFooterView);
401 }
402 mListView.addFooterView(mListFooterView, null, false);
403 }
404 mListFooterView.invalidate();
405
406 } else {
407 mGridView.removeFooterView(mGridFooterView);
408 mListView.removeFooterView(mListFooterView);
409 }
410 }
411
412 /**
413 * TODO doc
414 * @param text
415 */
416 protected void setFooterText(String text) {
417 if (text != null && text.length() > 0) {
418 ((TextView)mListFooterView.findViewById(R.id.footerText)).setText(text);
419 ((TextView)mGridFooterView.findViewById(R.id.footerText)).setText(text);
420 setFooterEnabled(true);
421
422 } else {
423 setFooterEnabled(false);
424 }
425 }
426
427 }