ExtendedListFragment.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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. package com.owncloud.android.ui.fragment;
  19. import java.util.ArrayList;
  20. import android.content.Context;
  21. import android.os.Bundle;
  22. import android.support.v4.widget.SwipeRefreshLayout;
  23. import android.view.LayoutInflater;
  24. import android.view.View;
  25. import android.view.ViewGroup;
  26. import android.widget.AbsListView;
  27. import android.widget.AdapterView;
  28. import android.widget.AdapterView.OnItemClickListener;
  29. import android.widget.GridView;
  30. import android.widget.ListAdapter;
  31. import android.widget.TextView;
  32. import com.actionbarsherlock.app.SherlockFragment;
  33. import com.owncloud.android.R;
  34. import com.owncloud.android.lib.common.utils.Log_OC;
  35. import com.owncloud.android.ui.ExtendedListView;
  36. import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
  37. import com.owncloud.android.ui.adapter.FileListListAdapter;
  38. import third_parties.in.srain.cube.GridViewWithHeaderAndFooter;
  39. /**
  40. * TODO extending SherlockListFragment instead of SherlockFragment
  41. */
  42. public class ExtendedListFragment extends SherlockFragment
  43. implements OnItemClickListener, OnEnforceableRefreshListener {
  44. private static final String TAG = ExtendedListFragment.class.getSimpleName();
  45. private static final String KEY_SAVED_LIST_POSITION = "SAVED_LIST_POSITION";
  46. private static final String KEY_INDEXES = "INDEXES";
  47. private static final String KEY_FIRST_POSITIONS= "FIRST_POSITIONS";
  48. private static final String KEY_TOPS = "TOPS";
  49. private static final String KEY_HEIGHT_CELL = "HEIGHT_CELL";
  50. private static final String KEY_EMPTY_LIST_MESSAGE = "EMPTY_LIST_MESSAGE";
  51. private SwipeRefreshLayout mRefreshListLayout;
  52. private SwipeRefreshLayout mRefreshGridLayout;
  53. private SwipeRefreshLayout mRefreshEmptyLayout;
  54. private TextView mEmptyListMessage;
  55. // Save the state of the scroll in browsing
  56. private ArrayList<Integer> mIndexes;
  57. private ArrayList<Integer> mFirstPositions;
  58. private ArrayList<Integer> mTops;
  59. private int mHeightCell = 0;
  60. private OnEnforceableRefreshListener mOnRefreshListener = null;
  61. protected AbsListView mCurrentListView;
  62. private ExtendedListView mListView;
  63. private View mListFooterView;
  64. private GridViewWithHeaderAndFooter mGridView;
  65. private View mGridFooterView;
  66. private ListAdapter mAdapter;
  67. protected void setListAdapter(ListAdapter listAdapter) {
  68. mAdapter = listAdapter;
  69. mCurrentListView.setAdapter(listAdapter);
  70. mCurrentListView.invalidate();
  71. }
  72. protected AbsListView getListView() {
  73. return mCurrentListView;
  74. }
  75. protected void switchToGridView() {
  76. if ((mCurrentListView == mListView)) {
  77. mListView.setAdapter(null);
  78. mRefreshListLayout.setVisibility(View.GONE);
  79. if (mAdapter instanceof FileListListAdapter) {
  80. ((FileListListAdapter) mAdapter).setGridMode(true);
  81. }
  82. mGridView.setAdapter(mAdapter);
  83. mRefreshGridLayout.setVisibility(View.VISIBLE);
  84. mCurrentListView = mGridView;
  85. }
  86. }
  87. protected void switchToListView() {
  88. if (mCurrentListView == mGridView) {
  89. mGridView.setAdapter(null);
  90. mRefreshGridLayout.setVisibility(View.GONE);
  91. if (mAdapter instanceof FileListListAdapter) {
  92. ((FileListListAdapter) mAdapter).setGridMode(false);
  93. }
  94. mListView.setAdapter(mAdapter);
  95. mRefreshListLayout.setVisibility(View.VISIBLE);
  96. mCurrentListView = mListView;
  97. }
  98. }
  99. @Override
  100. public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  101. Log_OC.d(TAG, "onCreateView");
  102. View v = inflater.inflate(R.layout.list_fragment, null);
  103. mListView = (ExtendedListView)(v.findViewById(R.id.list_root));
  104. mListView.setOnItemClickListener(this);
  105. mListFooterView = inflater.inflate(R.layout.list_footer, null, false);
  106. mGridView = (GridViewWithHeaderAndFooter) (v.findViewById(R.id.grid_root));
  107. mGridView.setNumColumns(GridView.AUTO_FIT);
  108. mGridView.setOnItemClickListener(this);
  109. mGridFooterView = inflater.inflate(R.layout.list_footer, null, false);
  110. if (savedInstanceState != null) {
  111. int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
  112. if (mCurrentListView == mListView) {
  113. Log_OC.v(TAG, "Setting and centering around list position " + referencePosition);
  114. mListView.setAndCenterSelection(referencePosition);
  115. } else {
  116. Log_OC.v(TAG, "Setting grid position " + referencePosition);
  117. mGridView.setSelection(referencePosition);
  118. }
  119. }
  120. // Pull-down to refresh layout
  121. mRefreshListLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_list);
  122. mRefreshGridLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_grid);
  123. mRefreshEmptyLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_empty);
  124. mEmptyListMessage = (TextView) v.findViewById(R.id.empty_list_view);
  125. onCreateSwipeToRefresh(mRefreshListLayout);
  126. onCreateSwipeToRefresh(mRefreshGridLayout);
  127. onCreateSwipeToRefresh(mRefreshEmptyLayout);
  128. mListView.setEmptyView(mRefreshEmptyLayout);
  129. mGridView.setEmptyView(mRefreshEmptyLayout);
  130. mCurrentListView = mListView; // list as default
  131. return v;
  132. }
  133. /**
  134. * {@inheritDoc}
  135. */
  136. @Override
  137. public void onActivityCreated(Bundle savedInstanceState) {
  138. super.onActivityCreated(savedInstanceState);
  139. if (savedInstanceState != null) {
  140. mIndexes = savedInstanceState.getIntegerArrayList(KEY_INDEXES);
  141. mFirstPositions = savedInstanceState.getIntegerArrayList(KEY_FIRST_POSITIONS);
  142. mTops = savedInstanceState.getIntegerArrayList(KEY_TOPS);
  143. mHeightCell = savedInstanceState.getInt(KEY_HEIGHT_CELL);
  144. setMessageForEmptyList(savedInstanceState.getString(KEY_EMPTY_LIST_MESSAGE));
  145. } else {
  146. mIndexes = new ArrayList<Integer>();
  147. mFirstPositions = new ArrayList<Integer>();
  148. mTops = new ArrayList<Integer>();
  149. mHeightCell = 0;
  150. }
  151. }
  152. @Override
  153. public void onSaveInstanceState(Bundle savedInstanceState) {
  154. super.onSaveInstanceState(savedInstanceState);
  155. Log_OC.d(TAG, "onSaveInstanceState()");
  156. savedInstanceState.putInt(KEY_SAVED_LIST_POSITION, getReferencePosition());
  157. savedInstanceState.putIntegerArrayList(KEY_INDEXES, mIndexes);
  158. savedInstanceState.putIntegerArrayList(KEY_FIRST_POSITIONS, mFirstPositions);
  159. savedInstanceState.putIntegerArrayList(KEY_TOPS, mTops);
  160. savedInstanceState.putInt(KEY_HEIGHT_CELL, mHeightCell);
  161. savedInstanceState.putString(KEY_EMPTY_LIST_MESSAGE, getEmptyViewText());
  162. }
  163. /**
  164. * Calculates the position of the item that will be used as a reference to
  165. * reposition the visible items in the list when the device is turned to
  166. * other position.
  167. *
  168. * The current policy is take as a reference the visible item in the center
  169. * of the screen.
  170. *
  171. * @return The position in the list of the visible item in the center of the
  172. * screen.
  173. */
  174. protected int getReferencePosition() {
  175. if (mCurrentListView != null) {
  176. return (mCurrentListView.getFirstVisiblePosition() + mCurrentListView.getLastVisiblePosition()) / 2;
  177. } else {
  178. return 0;
  179. }
  180. }
  181. /*
  182. * Restore index and position
  183. */
  184. protected void restoreIndexAndTopPosition() {
  185. if (mIndexes.size() > 0) {
  186. // needs to be checked; not every browse-up had a browse-down before
  187. int index = mIndexes.remove(mIndexes.size() - 1);
  188. final int firstPosition = mFirstPositions.remove(mFirstPositions.size() -1);
  189. int top = mTops.remove(mTops.size() - 1);
  190. Log_OC.v(TAG, "Setting selection to position: " + firstPosition + "; top: " + top + "; index: " + index);
  191. if (mCurrentListView == mListView) {
  192. if (mHeightCell*index <= mListView.getHeight()) {
  193. mListView.setSelectionFromTop(firstPosition, top);
  194. } else {
  195. mListView.setSelectionFromTop(index, 0);
  196. }
  197. } else {
  198. if (mHeightCell*index <= mGridView.getHeight()) {
  199. mGridView.setSelection(firstPosition);
  200. //mGridView.smoothScrollToPosition(firstPosition);
  201. } else {
  202. mGridView.setSelection(index);
  203. //mGridView.smoothScrollToPosition(index);
  204. }
  205. }
  206. }
  207. }
  208. /*
  209. * Save index and top position
  210. */
  211. protected void saveIndexAndTopPosition(int index) {
  212. mIndexes.add(index);
  213. int firstPosition = mCurrentListView.getFirstVisiblePosition();
  214. mFirstPositions.add(firstPosition);
  215. View view = mCurrentListView.getChildAt(0);
  216. int top = (view == null) ? 0 : view.getTop() ;
  217. mTops.add(top);
  218. // Save the height of a cell
  219. mHeightCell = (view == null || mHeightCell != 0) ? mHeightCell : view.getHeight();
  220. }
  221. @Override
  222. public void onItemClick (AdapterView<?> parent, View view, int position, long id) {
  223. // to be @overriden
  224. }
  225. @Override
  226. public void onRefresh() {
  227. mRefreshListLayout.setRefreshing(false);
  228. mRefreshGridLayout.setRefreshing(false);
  229. mRefreshEmptyLayout.setRefreshing(false);
  230. if (mOnRefreshListener != null) {
  231. mOnRefreshListener.onRefresh();
  232. }
  233. }
  234. public void setOnRefreshListener(OnEnforceableRefreshListener listener) {
  235. mOnRefreshListener = listener;
  236. }
  237. /**
  238. * Disables swipe gesture.
  239. *
  240. * Sets the 'enabled' state of the refresh layouts contained in the fragment.
  241. *
  242. * When 'false' is set, prevents user gestures but keeps the option to refresh programatically,
  243. *
  244. * @param enabled Desired state for capturing swipe gesture.
  245. */
  246. public void setSwipeEnabled(boolean enabled) {
  247. mRefreshListLayout.setEnabled(enabled);
  248. mRefreshGridLayout.setEnabled(enabled);
  249. mRefreshEmptyLayout.setEnabled(enabled);
  250. }
  251. /**
  252. * Set message for empty list view
  253. */
  254. public void setMessageForEmptyList(String message) {
  255. if (mEmptyListMessage != null) {
  256. mEmptyListMessage.setText(message);
  257. }
  258. }
  259. /**
  260. * Get the text of EmptyListMessage TextView
  261. *
  262. * @return String
  263. */
  264. public String getEmptyViewText() {
  265. return (mEmptyListMessage != null) ? mEmptyListMessage.getText().toString() : "";
  266. }
  267. private void onCreateSwipeToRefresh(SwipeRefreshLayout refreshLayout) {
  268. // Colors in animations: background
  269. refreshLayout.setColorScheme(R.color.background_color, R.color.background_color, R.color.background_color,
  270. R.color.background_color);
  271. refreshLayout.setOnRefreshListener(this);
  272. }
  273. @Override
  274. public void onRefresh(boolean ignoreETag) {
  275. mRefreshListLayout.setRefreshing(false);
  276. mRefreshGridLayout.setRefreshing(false);
  277. mRefreshEmptyLayout.setRefreshing(false);
  278. if (mOnRefreshListener != null) {
  279. mOnRefreshListener.onRefresh(ignoreETag);
  280. }
  281. }
  282. protected void setChoiceMode(int choiceMode) {
  283. mListView.setChoiceMode(choiceMode);
  284. mGridView.setChoiceMode(choiceMode);
  285. }
  286. protected void registerForContextMenu() {
  287. registerForContextMenu(mListView);
  288. registerForContextMenu(mGridView);
  289. mListView.setOnCreateContextMenuListener(this);
  290. mGridView.setOnCreateContextMenuListener(this);
  291. }
  292. /**
  293. * TODO doc
  294. * To be called before setAdapter, or GridViewWithHeaderAndFooter will throw an exception
  295. *
  296. * @param enabled
  297. */
  298. protected void setFooterEnabled(boolean enabled) {
  299. if (enabled) {
  300. if (mGridView.getFooterViewCount() == 0) {
  301. if (mGridFooterView.getParent() != null ) {
  302. ((ViewGroup) mGridFooterView.getParent()).removeView(mGridFooterView);
  303. }
  304. mGridView.addFooterView(mGridFooterView, null, false);
  305. }
  306. mGridFooterView.invalidate();
  307. if (mListView.getFooterViewsCount() == 0) {
  308. if (mListFooterView.getParent() != null ) {
  309. ((ViewGroup) mListFooterView.getParent()).removeView(mListFooterView);
  310. }
  311. mListView.addFooterView(mListFooterView, null, false);
  312. }
  313. mListFooterView.invalidate();
  314. } else {
  315. mGridView.removeFooterView(mGridFooterView);
  316. mListView.removeFooterView(mListFooterView);
  317. }
  318. }
  319. /**
  320. * TODO doc
  321. * @param text
  322. */
  323. protected void setFooterText(String text) {
  324. if (text != null && text.length() > 0) {
  325. ((TextView)mListFooterView.findViewById(R.id.footerText)).setText(text);
  326. ((TextView)mGridFooterView.findViewById(R.id.footerText)).setText(text);
  327. setFooterEnabled(true);
  328. } else {
  329. setFooterEnabled(false);
  330. }
  331. }
  332. }