FileDetailActivitiesFragment.java 13 KB

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