ExtendedListFragment.java 14 KB

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