NotificationsActivity.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Andy Scherzinger
  5. * @author Mario Danic
  6. * @author Chris Narkiewicz
  7. * Copyright (C) 2017 Andy Scherzinger
  8. * Copyright (C) 2017 Mario Danic
  9. * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
  10. *
  11. * This program is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Affero General Public License as published by
  13. * the Free Software Foundation, either version 3 of the License, or
  14. * at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU Affero General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. */
  24. package com.owncloud.android.ui.activity;
  25. import android.os.Bundle;
  26. import android.view.Menu;
  27. import android.view.MenuItem;
  28. import android.view.View;
  29. import com.google.android.material.snackbar.Snackbar;
  30. import com.nextcloud.client.account.User;
  31. import com.nextcloud.client.account.UserAccountManager;
  32. import com.nextcloud.client.jobs.NotificationWork;
  33. import com.nextcloud.client.network.ClientFactory;
  34. import com.nextcloud.java.util.Optional;
  35. import com.owncloud.android.R;
  36. import com.owncloud.android.databinding.NotificationsLayoutBinding;
  37. import com.owncloud.android.datamodel.ArbitraryDataProvider;
  38. import com.owncloud.android.lib.common.OwnCloudClient;
  39. import com.owncloud.android.lib.common.operations.RemoteOperation;
  40. import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  41. import com.owncloud.android.lib.common.utils.Log_OC;
  42. import com.owncloud.android.lib.resources.notifications.GetNotificationsRemoteOperation;
  43. import com.owncloud.android.lib.resources.notifications.models.Notification;
  44. import com.owncloud.android.ui.adapter.NotificationListAdapter;
  45. import com.owncloud.android.ui.asynctasks.DeleteAllNotificationsTask;
  46. import com.owncloud.android.ui.notifications.NotificationsContract;
  47. import com.owncloud.android.utils.DisplayUtils;
  48. import com.owncloud.android.utils.PushUtils;
  49. import com.owncloud.android.utils.theme.ThemeLayoutUtils;
  50. import java.util.List;
  51. import javax.inject.Inject;
  52. import androidx.annotation.VisibleForTesting;
  53. import androidx.recyclerview.widget.LinearLayoutManager;
  54. /**
  55. * Activity displaying all server side stored notification items.
  56. */
  57. public class NotificationsActivity extends DrawerActivity implements NotificationsContract.View {
  58. private static final String TAG = NotificationsActivity.class.getSimpleName();
  59. private NotificationsLayoutBinding binding;
  60. private NotificationListAdapter adapter;
  61. private Snackbar snackbar;
  62. private OwnCloudClient client;
  63. private Optional<User> optionalUser;
  64. @Inject ClientFactory clientFactory;
  65. @Override
  66. protected void onCreate(Bundle savedInstanceState) {
  67. Log_OC.v(TAG, "onCreate() start");
  68. super.onCreate(savedInstanceState);
  69. binding = NotificationsLayoutBinding.inflate(getLayoutInflater());
  70. setContentView(binding.getRoot());
  71. optionalUser = getUser();
  72. // use account from intent (opened via android notification can have a different account than current one)
  73. if (getIntent() != null && getIntent().getExtras() != null) {
  74. String accountName = getIntent().getExtras().getString(NotificationWork.KEY_NOTIFICATION_ACCOUNT);
  75. if(accountName != null && optionalUser.isPresent()) {
  76. User user = optionalUser.get();
  77. if (user.getAccountName().equalsIgnoreCase(accountName)) {
  78. accountManager.setCurrentOwnCloudAccount(accountName);
  79. setUser(getUserAccountManager().getUser());
  80. optionalUser = getUser();
  81. }
  82. }
  83. }
  84. // setup toolbar
  85. setupToolbar();
  86. updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_notifications));
  87. ThemeLayoutUtils.colorSwipeRefreshLayout(this, binding.swipeContainingList);
  88. ThemeLayoutUtils.colorSwipeRefreshLayout(this, binding.swipeContainingEmpty);
  89. // setup drawer
  90. setupDrawer(R.id.nav_notifications);
  91. if (!optionalUser.isPresent()) {
  92. // show error
  93. runOnUiThread(() -> setEmptyContent(
  94. getString(R.string.notifications_no_results_headline),
  95. getString(R.string.account_not_found))
  96. );
  97. return;
  98. }
  99. binding.swipeContainingList.setOnRefreshListener(() -> {
  100. setLoadingMessage();
  101. binding.swipeContainingList.setRefreshing(true);
  102. fetchAndSetData();
  103. });
  104. binding.swipeContainingEmpty.setOnRefreshListener(() -> {
  105. setLoadingMessageEmpty();
  106. fetchAndSetData();
  107. });
  108. setupPushWarning();
  109. setupContent();
  110. }
  111. private void setupPushWarning() {
  112. if (!getResources().getBoolean(R.bool.show_push_warning)) {
  113. return;
  114. }
  115. if (snackbar != null) {
  116. if (!snackbar.isShown()) {
  117. snackbar.show();
  118. }
  119. } else {
  120. String pushUrl = getResources().getString(R.string.push_server_url);
  121. if (pushUrl.isEmpty()) {
  122. snackbar = Snackbar.make(binding.emptyList.emptyListView,
  123. R.string.push_notifications_not_implemented,
  124. Snackbar.LENGTH_INDEFINITE);
  125. } else {
  126. final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
  127. final String accountName = optionalUser.isPresent() ? optionalUser.get().getAccountName() : "";
  128. final boolean usesOldLogin = arbitraryDataProvider.getBooleanValue(accountName,
  129. UserAccountManager.ACCOUNT_USES_STANDARD_PASSWORD);
  130. if (usesOldLogin) {
  131. snackbar = Snackbar.make(binding.emptyList.emptyListView,
  132. R.string.push_notifications_old_login,
  133. Snackbar.LENGTH_INDEFINITE);
  134. } else {
  135. String pushValue = arbitraryDataProvider.getValue(accountName, PushUtils.KEY_PUSH);
  136. if (pushValue == null || pushValue.isEmpty()) {
  137. snackbar = Snackbar.make(binding.emptyList.emptyListView,
  138. R.string.push_notifications_temp_error,
  139. Snackbar.LENGTH_INDEFINITE);
  140. }
  141. }
  142. }
  143. if (snackbar != null && !snackbar.isShown()) {
  144. snackbar.show();
  145. }
  146. }
  147. }
  148. @Override
  149. public void openDrawer() {
  150. super.openDrawer();
  151. if (snackbar != null && snackbar.isShown()) {
  152. snackbar.dismiss();
  153. }
  154. }
  155. @Override
  156. public void closeDrawer() {
  157. super.closeDrawer();
  158. setupPushWarning();
  159. }
  160. /**
  161. * sets up the UI elements and loads all notification items.
  162. */
  163. private void setupContent() {
  164. binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_notification);
  165. setLoadingMessageEmpty();
  166. LinearLayoutManager layoutManager = new LinearLayoutManager(this);
  167. binding.list.setLayoutManager(layoutManager);
  168. fetchAndSetData();
  169. }
  170. @VisibleForTesting
  171. public void populateList(List<Notification> notifications) {
  172. initializeAdapter();
  173. adapter.setNotificationItems(notifications);
  174. binding.loadingContent.setVisibility(View.GONE);
  175. if (notifications.size() > 0) {
  176. binding.swipeContainingEmpty.setVisibility(View.GONE);
  177. binding.swipeContainingList.setVisibility(View.VISIBLE);
  178. } else {
  179. setEmptyContent(
  180. getString(R.string.notifications_no_results_headline),
  181. getString(R.string.notifications_no_results_message)
  182. );
  183. binding.swipeContainingList.setVisibility(View.GONE);
  184. binding.swipeContainingEmpty.setVisibility(View.VISIBLE);
  185. }
  186. }
  187. private void fetchAndSetData() {
  188. Thread t = new Thread(() -> {
  189. initializeAdapter();
  190. RemoteOperation getRemoteNotificationOperation = new GetNotificationsRemoteOperation();
  191. final RemoteOperationResult result = getRemoteNotificationOperation.execute(client);
  192. if (result.isSuccess() && result.getNotificationData() != null) {
  193. final List<Notification> notifications = result.getNotificationData();
  194. runOnUiThread(() -> populateList(notifications));
  195. } else {
  196. Log_OC.d(TAG, result.getLogMessage());
  197. // show error
  198. runOnUiThread(() -> setEmptyContent(getString(R.string.notifications_no_results_headline), result.getLogMessage()));
  199. }
  200. hideRefreshLayoutLoader();
  201. });
  202. t.start();
  203. }
  204. private void initializeClient() {
  205. if (client == null && optionalUser.isPresent()) {
  206. try {
  207. User user = optionalUser.get();
  208. client = clientFactory.create(user);
  209. } catch (ClientFactory.CreationException e) {
  210. Log_OC.e(TAG, "Error initializing client", e);
  211. }
  212. }
  213. }
  214. private void initializeAdapter() {
  215. initializeClient();
  216. if (adapter == null) {
  217. adapter = new NotificationListAdapter(client, this);
  218. binding.list.setAdapter(adapter);
  219. }
  220. }
  221. private void hideRefreshLayoutLoader() {
  222. runOnUiThread(() -> {
  223. binding.swipeContainingList.setRefreshing(false);
  224. binding.swipeContainingEmpty.setRefreshing(false);
  225. });
  226. }
  227. public boolean onCreateOptionsMenu(Menu menu) {
  228. getMenuInflater().inflate(R.menu.activity_notifications, menu);
  229. return true;
  230. }
  231. @Override
  232. public boolean onOptionsItemSelected(MenuItem item) {
  233. boolean retval = true;
  234. int itemId = item.getItemId();
  235. if (itemId == android.R.id.home) {
  236. if (isDrawerOpen()) {
  237. closeDrawer();
  238. } else {
  239. openDrawer();
  240. }
  241. } else if (itemId == R.id.action_empty_notifications) {
  242. new DeleteAllNotificationsTask(client, this).execute();
  243. } else {
  244. retval = super.onOptionsItemSelected(item);
  245. }
  246. return retval;
  247. }
  248. private void setLoadingMessage() {
  249. binding.swipeContainingEmpty.setVisibility(View.GONE);
  250. }
  251. @VisibleForTesting
  252. public void setLoadingMessageEmpty() {
  253. binding.swipeContainingList.setVisibility(View.GONE);
  254. binding.emptyList.emptyListView.setVisibility(View.GONE);
  255. binding.loadingContent.setVisibility(View.VISIBLE);
  256. }
  257. @VisibleForTesting
  258. public void setEmptyContent(String headline, String message) {
  259. binding.swipeContainingList.setVisibility(View.GONE);
  260. binding.loadingContent.setVisibility(View.GONE);
  261. binding.swipeContainingEmpty.setVisibility(View.VISIBLE);
  262. binding.emptyList.emptyListView.setVisibility(View.VISIBLE);
  263. binding.emptyList.emptyListViewHeadline.setText(headline);
  264. binding.emptyList.emptyListViewText.setText(message);
  265. binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_notification);
  266. binding.emptyList.emptyListViewText.setVisibility(View.VISIBLE);
  267. binding.emptyList.emptyListIcon.setVisibility(View.VISIBLE);
  268. }
  269. @Override
  270. protected void onResume() {
  271. super.onResume();
  272. setDrawerMenuItemChecked(R.id.nav_notifications);
  273. }
  274. @Override
  275. public void onRemovedNotification(boolean isSuccess) {
  276. if (!isSuccess) {
  277. DisplayUtils.showSnackMessage(this, getString(R.string.remove_notification_failed));
  278. fetchAndSetData();
  279. }
  280. }
  281. @Override
  282. public void removeNotification(NotificationListAdapter.NotificationViewHolder holder) {
  283. adapter.removeNotification(holder);
  284. if (adapter.getItemCount() == 0) {
  285. setEmptyContent(getString(R.string.notifications_no_results_headline), getString(R.string.notifications_no_results_message));
  286. binding.swipeContainingList.setVisibility(View.GONE);
  287. binding.loadingContent.setVisibility(View.GONE);
  288. binding.swipeContainingEmpty.setVisibility(View.VISIBLE);
  289. }
  290. }
  291. @Override
  292. public void onRemovedAllNotifications(boolean isSuccess) {
  293. if (isSuccess) {
  294. adapter.removeAllNotifications();
  295. setEmptyContent(getString(R.string.notifications_no_results_headline), getString(R.string.notifications_no_results_message));
  296. binding.loadingContent.setVisibility(View.GONE);
  297. binding.swipeContainingList.setVisibility(View.GONE);
  298. binding.swipeContainingEmpty.setVisibility(View.VISIBLE);
  299. } else {
  300. DisplayUtils.showSnackMessage(this, getString(R.string.clear_notifications_failed));
  301. }
  302. }
  303. @Override
  304. public void onActionCallback(boolean isSuccess,
  305. Notification notification,
  306. NotificationListAdapter.NotificationViewHolder holder) {
  307. if (isSuccess) {
  308. adapter.removeNotification(holder);
  309. } else {
  310. adapter.setButtons(holder, notification);
  311. DisplayUtils.showSnackMessage(this, getString(R.string.notification_action_failed));
  312. }
  313. }
  314. }