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