FileDetailActivitiesFragment.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Andy Scherzinger
  5. * Copyright (C) 2018 Andy Scherzinger
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  9. * License as published by the Free Software Foundation; either
  10. * version 3 of the License, or any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public
  18. * License along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. package com.owncloud.android.ui.fragment;
  21. import android.accounts.Account;
  22. import android.accounts.AuthenticatorException;
  23. import android.accounts.OperationCanceledException;
  24. import android.content.Context;
  25. import android.graphics.PorterDuff;
  26. import android.os.Bundle;
  27. import android.support.annotation.NonNull;
  28. import android.support.v4.app.Fragment;
  29. import android.support.v4.widget.SwipeRefreshLayout;
  30. import android.support.v7.widget.LinearLayoutManager;
  31. import android.support.v7.widget.RecyclerView;
  32. import android.view.LayoutInflater;
  33. import android.view.View;
  34. import android.view.ViewGroup;
  35. import android.widget.ImageView;
  36. import android.widget.LinearLayout;
  37. import android.widget.ProgressBar;
  38. import android.widget.TextView;
  39. import com.owncloud.android.MainApp;
  40. import com.owncloud.android.R;
  41. import com.owncloud.android.authentication.AccountUtils;
  42. import com.owncloud.android.datamodel.FileDataStorageManager;
  43. import com.owncloud.android.datamodel.OCFile;
  44. import com.owncloud.android.lib.common.OwnCloudAccount;
  45. import com.owncloud.android.lib.common.OwnCloudClient;
  46. import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
  47. import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  48. import com.owncloud.android.lib.common.utils.Log_OC;
  49. import com.owncloud.android.lib.resources.activities.GetRemoteActivitiesOperation;
  50. import com.owncloud.android.lib.resources.activities.models.RichObject;
  51. import com.owncloud.android.ui.activity.FileActivity;
  52. import com.owncloud.android.ui.adapter.ActivityListAdapter;
  53. import com.owncloud.android.ui.interfaces.ActivityListInterface;
  54. import com.owncloud.android.utils.ThemeUtils;
  55. import java.io.IOException;
  56. import java.util.ArrayList;
  57. import java.util.List;
  58. import butterknife.BindString;
  59. import butterknife.BindView;
  60. import butterknife.ButterKnife;
  61. import butterknife.Unbinder;
  62. public class FileDetailActivitiesFragment extends Fragment implements ActivityListInterface {
  63. private static final String TAG = FileDetailActivitiesFragment.class.getSimpleName();
  64. private static final String ARG_FILE = "FILE";
  65. private static final String ARG_ACCOUNT = "ACCOUNT";
  66. private ActivityListAdapter adapter;
  67. private Unbinder unbinder;
  68. private OwnCloudClient ownCloudClient;
  69. private OCFile file;
  70. private Account account;
  71. private String nextPageUrl;
  72. private boolean isLoadingActivities;
  73. @BindView(R.id.empty_list_view)
  74. public LinearLayout emptyContentContainer;
  75. @BindView(R.id.swipe_containing_list)
  76. public SwipeRefreshLayout swipeListRefreshLayout;
  77. @BindView(R.id.swipe_containing_empty)
  78. public SwipeRefreshLayout swipeEmptyListRefreshLayout;
  79. @BindView(R.id.empty_list_view_text)
  80. public TextView emptyContentMessage;
  81. @BindView(R.id.empty_list_view_headline)
  82. public TextView emptyContentHeadline;
  83. @BindView(R.id.empty_list_icon)
  84. public ImageView emptyContentIcon;
  85. @BindView(R.id.empty_list_progress)
  86. public ProgressBar emptyContentProgressBar;
  87. @BindView(android.R.id.list)
  88. public RecyclerView recyclerView;
  89. @BindString(R.string.activities_no_results_headline)
  90. public String noResultsHeadline;
  91. @BindString(R.string.activities_no_results_message)
  92. public String noResultsMessage;
  93. public static FileDetailActivitiesFragment newInstance(OCFile file, Account account) {
  94. FileDetailActivitiesFragment fragment = new FileDetailActivitiesFragment();
  95. Bundle args = new Bundle();
  96. args.putParcelable(ARG_FILE, file);
  97. args.putParcelable(ARG_ACCOUNT, account);
  98. fragment.setArguments(args);
  99. return fragment;
  100. }
  101. @Override
  102. public View onCreateView(@NonNull LayoutInflater inflater,
  103. ViewGroup container,
  104. Bundle savedInstanceState) {
  105. file = getArguments().getParcelable(ARG_FILE);
  106. account = getArguments().getParcelable(ARG_ACCOUNT);
  107. if (savedInstanceState != null) {
  108. file = savedInstanceState.getParcelable(FileActivity.EXTRA_FILE);
  109. account = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
  110. }
  111. View view = inflater.inflate(R.layout.file_details_activities_fragment, container, false);
  112. unbinder = ButterKnife.bind(this, view);
  113. setupView();
  114. onCreateSwipeToRefresh(swipeEmptyListRefreshLayout);
  115. onCreateSwipeToRefresh(swipeListRefreshLayout);
  116. fetchAndSetData(null);
  117. swipeListRefreshLayout.setOnRefreshListener(() -> {
  118. setLoadingMessage();
  119. if (swipeListRefreshLayout != null && swipeListRefreshLayout.isRefreshing()) {
  120. swipeListRefreshLayout.setRefreshing(false);
  121. }
  122. fetchAndSetData(null);
  123. }
  124. );
  125. swipeEmptyListRefreshLayout.setOnRefreshListener(() -> {
  126. setLoadingMessage();
  127. if (swipeEmptyListRefreshLayout != null && swipeEmptyListRefreshLayout.isRefreshing()) {
  128. swipeEmptyListRefreshLayout.setRefreshing(false);
  129. }
  130. fetchAndSetData(null);
  131. }
  132. );
  133. return view;
  134. }
  135. private void setLoadingMessage() {
  136. emptyContentHeadline.setText(R.string.file_list_loading);
  137. emptyContentMessage.setText("");
  138. emptyContentIcon.setVisibility(View.GONE);
  139. emptyContentProgressBar.setVisibility(View.VISIBLE);
  140. }
  141. @Override
  142. public void onDestroy() {
  143. super.onDestroy();
  144. unbinder.unbind();
  145. }
  146. private void setupView() {
  147. FileDataStorageManager storageManager = new FileDataStorageManager(account, getActivity().getContentResolver());
  148. emptyContentProgressBar.getIndeterminateDrawable().setColorFilter(ThemeUtils.primaryAccentColor(getContext()),
  149. PorterDuff.Mode.SRC_IN);
  150. emptyContentIcon.setImageDrawable(getResources().getDrawable(R.drawable.ic_activity_light_grey));
  151. adapter = new ActivityListAdapter(getContext(), this, storageManager);
  152. recyclerView.setAdapter(adapter);
  153. LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
  154. recyclerView.setLayoutManager(layoutManager);
  155. recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
  156. @Override
  157. public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
  158. super.onScrolled(recyclerView, dx, dy);
  159. int visibleItemCount = recyclerView.getChildCount();
  160. int totalItemCount = layoutManager.getItemCount();
  161. int firstVisibleItemIndex = layoutManager.findFirstVisibleItemPosition();
  162. // synchronize loading state when item count changes
  163. if (!isLoadingActivities && (totalItemCount - visibleItemCount) <= (firstVisibleItemIndex + 5)
  164. && nextPageUrl != null && !nextPageUrl.isEmpty()) {
  165. // Almost reached the end, continue to load new activities
  166. fetchAndSetData(nextPageUrl);
  167. }
  168. }
  169. });
  170. }
  171. /**
  172. * @param pageUrl String
  173. */
  174. private void fetchAndSetData(String pageUrl) {
  175. final Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext());
  176. final Context context = MainApp.getAppContext();
  177. Thread t = new Thread(() -> {
  178. OwnCloudAccount ocAccount;
  179. try {
  180. ocAccount = new OwnCloudAccount(currentAccount, context);
  181. ownCloudClient = OwnCloudClientManagerFactory.getDefaultSingleton().
  182. getClientFor(ocAccount, MainApp.getAppContext());
  183. ownCloudClient.setOwnCloudVersion(AccountUtils.getServerVersion(currentAccount));
  184. isLoadingActivities = true;
  185. GetRemoteActivitiesOperation getRemoteNotificationOperation = new GetRemoteActivitiesOperation(
  186. file.getLocalId());
  187. if (pageUrl != null) {
  188. getRemoteNotificationOperation.setNextUrl(pageUrl);
  189. }
  190. Log_OC.d(TAG, "BEFORE getRemoteActivitiesOperation.execute");
  191. final RemoteOperationResult result = getRemoteNotificationOperation.execute(ownCloudClient);
  192. if (result.isSuccess() && result.getData() != null) {
  193. final ArrayList<Object> data = result.getData();
  194. final ArrayList<Object> activities = (ArrayList) data.get(0);
  195. nextPageUrl = (String) data.get(1);
  196. getActivity().runOnUiThread(() -> {
  197. populateList(activities, ownCloudClient, pageUrl == null);
  198. if (activities.size() > 0) {
  199. swipeEmptyListRefreshLayout.setVisibility(View.GONE);
  200. swipeListRefreshLayout.setVisibility(View.VISIBLE);
  201. } else {
  202. setEmptyContent(noResultsHeadline, noResultsMessage);
  203. swipeListRefreshLayout.setVisibility(View.GONE);
  204. swipeEmptyListRefreshLayout.setVisibility(View.VISIBLE);
  205. }
  206. isLoadingActivities = false;
  207. });
  208. } else {
  209. Log_OC.d(TAG, result.getLogMessage());
  210. // show error
  211. String logMessage = result.getLogMessage();
  212. if (result.getHttpCode() == 304) {
  213. logMessage = noResultsMessage;
  214. }
  215. final String finalLogMessage = logMessage;
  216. getActivity().runOnUiThread(() -> {
  217. setEmptyContent(noResultsHeadline, finalLogMessage);
  218. isLoadingActivities = false;
  219. //setIndeterminate(isLoadingActivities);
  220. });
  221. }
  222. hideRefreshLayoutLoader();
  223. } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
  224. Log_OC.e(TAG, "Account not found", e);
  225. } catch (IOException e) {
  226. Log_OC.e(TAG, "IO error", e);
  227. } catch (OperationCanceledException e) {
  228. Log_OC.e(TAG, "Operation has been canceled", e);
  229. } catch (AuthenticatorException e) {
  230. Log_OC.e(TAG, "Authentication Exception", e);
  231. }
  232. }
  233. );
  234. t.start();
  235. }
  236. private void populateList(List<Object> activities, OwnCloudClient mClient, boolean clear) {
  237. adapter.setActivityItems(activities, mClient, clear);
  238. }
  239. private void setEmptyContent(String headline, String message) {
  240. if (emptyContentContainer != null && emptyContentMessage != null) {
  241. emptyContentHeadline.setText(headline);
  242. emptyContentMessage.setText(message);
  243. emptyContentProgressBar.setVisibility(View.GONE);
  244. emptyContentIcon.setVisibility(View.VISIBLE);
  245. }
  246. }
  247. private void hideRefreshLayoutLoader() {
  248. getActivity().runOnUiThread(() -> {
  249. if (swipeListRefreshLayout != null) {
  250. swipeListRefreshLayout.setRefreshing(false);
  251. }
  252. if (swipeEmptyListRefreshLayout != null) {
  253. swipeEmptyListRefreshLayout.setRefreshing(false);
  254. }
  255. isLoadingActivities = false;
  256. //setIndeterminate(isLoadingActivities);
  257. });
  258. }
  259. protected void onCreateSwipeToRefresh(SwipeRefreshLayout refreshLayout) {
  260. int primaryColor = ThemeUtils.primaryColor(getContext());
  261. int darkColor = ThemeUtils.primaryDarkColor(getContext());
  262. int accentColor = ThemeUtils.primaryAccentColor(getContext());
  263. // Colors in animations
  264. refreshLayout.setColorSchemeColors(accentColor, primaryColor, darkColor);
  265. }
  266. @Override
  267. public void onActivityClicked(RichObject richObject) {
  268. // TODO implement activity click
  269. }
  270. }