ExtendedListFragment.java 15 KB

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