Jelajahi Sumber

Merge pull request #3607 from nextcloud/notificationList

Delete notification in notification list
Andy Scherzinger 6 tahun lalu
induk
melakukan
77947fb6bd

+ 52 - 3
src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java

@@ -28,6 +28,7 @@ import android.accounts.OperationCanceledException;
 import android.content.Intent;
 import android.content.Intent;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuff;
 import android.os.Bundle;
 import android.os.Bundle;
+import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.ImageView;
@@ -47,9 +48,11 @@ import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
 import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.lib.resources.notifications.GetRemoteNotificationsOperation;
+import com.owncloud.android.lib.resources.notifications.GetNotificationsRemoteOperation;
 import com.owncloud.android.lib.resources.notifications.models.Notification;
 import com.owncloud.android.lib.resources.notifications.models.Notification;
 import com.owncloud.android.ui.adapter.NotificationListAdapter;
 import com.owncloud.android.ui.adapter.NotificationListAdapter;
+import com.owncloud.android.ui.asynctasks.DeleteAllNotificationsTask;
+import com.owncloud.android.ui.notifications.NotificationsContract;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.PushUtils;
 import com.owncloud.android.utils.PushUtils;
 import com.owncloud.android.utils.ThemeUtils;
 import com.owncloud.android.utils.ThemeUtils;
@@ -68,7 +71,7 @@ import butterknife.Unbinder;
 /**
 /**
  * Activity displaying all server side stored notification items.
  * Activity displaying all server side stored notification items.
  */
  */
-public class NotificationsActivity extends FileActivity {
+public class NotificationsActivity extends FileActivity implements NotificationsContract.View {
 
 
     private static final String TAG = NotificationsActivity.class.getSimpleName();
     private static final String TAG = NotificationsActivity.class.getSimpleName();
 
 
@@ -269,7 +272,7 @@ public class NotificationsActivity extends FileActivity {
                 recyclerView.setAdapter(adapter);
                 recyclerView.setAdapter(adapter);
             }
             }
 
 
-            RemoteOperation getRemoteNotificationOperation = new GetRemoteNotificationsOperation();
+            RemoteOperation getRemoteNotificationOperation = new GetNotificationsRemoteOperation();
             final RemoteOperationResult result = getRemoteNotificationOperation.execute(client);
             final RemoteOperationResult result = getRemoteNotificationOperation.execute(client);
 
 
             if (result.isSuccess() && result.getNotificationData() != null) {
             if (result.isSuccess() && result.getNotificationData() != null) {
@@ -305,6 +308,11 @@ public class NotificationsActivity extends FileActivity {
         });
         });
     }
     }
 
 
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.notifications_actions_menu, menu);
+
+        return true;
+    }
 
 
     @Override
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
     public boolean onOptionsItemSelected(MenuItem item) {
@@ -319,6 +327,10 @@ public class NotificationsActivity extends FileActivity {
                 }
                 }
                 break;
                 break;
 
 
+            case R.id.action_empty_notifications:
+                new DeleteAllNotificationsTask(client, this).execute();
+                break;
+
             default:
             default:
                 retval = super.onOptionsItemSelected(item);
                 retval = super.onOptionsItemSelected(item);
                 break;
                 break;
@@ -353,4 +365,41 @@ public class NotificationsActivity extends FileActivity {
 
 
         setDrawerMenuItemChecked(R.id.nav_notifications);
         setDrawerMenuItemChecked(R.id.nav_notifications);
     }
     }
+
+    @Override
+    public void onRemovedNotification(boolean isSuccess, NotificationListAdapter.NotificationViewHolder holder) {
+        if (isSuccess) {
+            adapter.removeNotification(holder);
+
+            if (adapter.getItemCount() == 0) {
+                setEmptyContent(noResultsHeadline, noResultsMessage);
+                swipeListRefreshLayout.setVisibility(View.GONE);
+                swipeEmptyListRefreshLayout.setVisibility(View.VISIBLE);
+            }
+        } else {
+            DisplayUtils.showSnackMessage(this, getString(R.string.remove_notification_failed));
+        }
+    }
+
+    @Override
+    public void onRemovedAllNotifications(boolean isSuccess) {
+        if (isSuccess) {
+            adapter.removeAllNotifications();
+            setEmptyContent(noResultsHeadline, noResultsMessage);
+            swipeListRefreshLayout.setVisibility(View.GONE);
+            swipeEmptyListRefreshLayout.setVisibility(View.VISIBLE);
+        } else {
+            DisplayUtils.showSnackMessage(this, getString(R.string.clear_notifications_failed));
+        }
+    }
+
+    @Override
+    public void onActionCallback(boolean isSuccess, NotificationListAdapter.NotificationViewHolder holder) {
+        if (isSuccess) {
+            adapter.removeNotification(holder);
+        } else {
+            adapter.setButtonEnabled(holder, true);
+            DisplayUtils.showSnackMessage(this, getString(R.string.notification_action_failed));
+        }
+    }
 }
 }

+ 22 - 70
src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java

@@ -27,7 +27,6 @@ import android.graphics.PorterDuff;
 import android.graphics.Typeface;
 import android.graphics.Typeface;
 import android.graphics.drawable.PictureDrawable;
 import android.graphics.drawable.PictureDrawable;
 import android.net.Uri;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.text.Spannable;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 import android.text.TextUtils;
@@ -50,25 +49,18 @@ import com.caverock.androidsvg.SVG;
 import com.google.android.material.button.MaterialButton;
 import com.google.android.material.button.MaterialButton;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.OwnCloudClient;
-import com.owncloud.android.lib.common.operations.RemoteOperation;
-import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.notifications.models.Action;
 import com.owncloud.android.lib.resources.notifications.models.Action;
 import com.owncloud.android.lib.resources.notifications.models.Notification;
 import com.owncloud.android.lib.resources.notifications.models.Notification;
 import com.owncloud.android.lib.resources.notifications.models.RichObject;
 import com.owncloud.android.lib.resources.notifications.models.RichObject;
 import com.owncloud.android.ui.activity.NotificationsActivity;
 import com.owncloud.android.ui.activity.NotificationsActivity;
+import com.owncloud.android.ui.asynctasks.DeleteNotificationTask;
+import com.owncloud.android.ui.asynctasks.NotificationExecuteActionTask;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.ThemeUtils;
 import com.owncloud.android.utils.ThemeUtils;
 import com.owncloud.android.utils.svg.SvgDecoder;
 import com.owncloud.android.utils.svg.SvgDecoder;
 import com.owncloud.android.utils.svg.SvgDrawableTranscoder;
 import com.owncloud.android.utils.svg.SvgDrawableTranscoder;
 import com.owncloud.android.utils.svg.SvgSoftwareLayerSetter;
 import com.owncloud.android.utils.svg.SvgSoftwareLayerSetter;
 
 
-import org.apache.commons.httpclient.HttpMethod;
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.methods.DeleteMethod;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.apache.commons.httpclient.methods.PostMethod;
-
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
@@ -141,7 +133,7 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
         MaterialButton button;
         MaterialButton button;
 
 
         Resources resources = notificationsActivity.getResources();
         Resources resources = notificationsActivity.getResources();
-
+        NotificationExecuteActionTask task = new NotificationExecuteActionTask(client, holder, notificationsActivity);
 
 
         for (Action action : notification.getActions()) {
         for (Action action : notification.getActions()) {
             button = new MaterialButton(notificationsActivity);
             button = new MaterialButton(notificationsActivity);
@@ -175,12 +167,14 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
 
 
             button.setOnClickListener(v -> {
             button.setOnClickListener(v -> {
                 setButtonEnabled(holder, false);
                 setButtonEnabled(holder, false);
-
-                new ExecuteActionTask(holder).execute(action);
+                task.execute(action);
             });
             });
 
 
             holder.buttons.addView(button);
             holder.buttons.addView(button);
         }
         }
+
+        holder.dismiss.setOnClickListener(v -> new DeleteNotificationTask(client, notification, holder,
+                                                                          notificationsActivity).execute());
     }
     }
 
 
     private SpannableStringBuilder makeSpecialPartsBold(Notification notification) {
     private SpannableStringBuilder makeSpecialPartsBold(Notification notification) {
@@ -211,67 +205,23 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
         return ssb;
         return ssb;
     }
     }
 
 
-    private class ExecuteActionTask extends AsyncTask<Action, Void, Boolean> {
-
-        private NotificationViewHolder holder;
-
-        ExecuteActionTask(NotificationViewHolder holder) {
-            this.holder = holder;
-        }
-
-        @Override
-        protected Boolean doInBackground(Action... actions) {
-            HttpMethod method;
-            Action action = actions[0];
-
-            switch (action.type) {
-                case "GET":
-                    method = new GetMethod(action.link);
-                    break;
-
-                case "POST":
-                    method = new PostMethod(action.link);
-                    break;
-
-                case "DELETE":
-                    method = new DeleteMethod(action.link);
-                    break;
-
-                default:
-                    // do nothing
-                    return false;
-            }
-
-            method.setRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE);
-
-            int status;
-            try {
-                status = client.executeMethod(method);
-            } catch (IOException e) {
-                Log_OC.e(TAG, "Execution of notification action failed: " + e);
-                return false;
-            }
+    public void removeNotification(NotificationViewHolder holder) {
+        int position = holder.getAdapterPosition();
 
 
-            return status == HttpStatus.SC_OK || status == HttpStatus.SC_ACCEPTED;
+        if (position >= 0 && position < notificationsList.size()) {
+            notificationsList.remove(position);
+            notifyItemRemoved(position);
+            notifyItemRangeChanged(position, notificationsList.size());
         }
         }
+    }
 
 
-        @Override
-        protected void onPostExecute(Boolean success) {
-            if (success) {
-                int position = holder.getAdapterPosition();
-
-                if (position >= 0 && position < notificationsList.size()) {
-                    notificationsList.remove(position);
-                    notifyItemRemoved(position);
-                }
-            } else {
-                setButtonEnabled(holder, true);
-                DisplayUtils.showSnackMessage(notificationsActivity, "Failed to execute action!");
-            }
-        }
+    public void removeAllNotifications() {
+        notificationsList.clear();
+        notifyDataSetChanged();
     }
     }
 
 
-    private void setButtonEnabled(NotificationViewHolder holder, boolean enabled) {
+
+    public void setButtonEnabled(NotificationViewHolder holder, boolean enabled) {
         for (int i = 0; i < holder.buttons.getChildCount(); i++) {
         for (int i = 0; i < holder.buttons.getChildCount(); i++) {
             holder.buttons.getChildAt(i).setEnabled(enabled);
             holder.buttons.getChildAt(i).setEnabled(enabled);
         }
         }
@@ -310,7 +260,7 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
         return notificationsList.size();
         return notificationsList.size();
     }
     }
 
 
-    static class NotificationViewHolder extends RecyclerView.ViewHolder {
+    public static class NotificationViewHolder extends RecyclerView.ViewHolder {
         @BindView(R.id.notification_icon)
         @BindView(R.id.notification_icon)
         public ImageView icon;
         public ImageView icon;
         @BindView(R.id.notification_subject)
         @BindView(R.id.notification_subject)
@@ -321,6 +271,8 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
         public TextView dateTime;
         public TextView dateTime;
         @BindView(R.id.notification_buttons)
         @BindView(R.id.notification_buttons)
         public LinearLayout buttons;
         public LinearLayout buttons;
+        @BindView(R.id.notification_dismiss)
+        public ImageView dismiss;
 
 
         private NotificationViewHolder(View itemView) {
         private NotificationViewHolder(View itemView) {
             super(itemView);
             super(itemView);

+ 54 - 0
src/main/java/com/owncloud/android/ui/asynctasks/DeleteAllNotificationsTask.java

@@ -0,0 +1,54 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2019 Tobias Kaminsky
+ * Copyright (C) 2019 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.asynctasks;
+
+import android.os.AsyncTask;
+
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.resources.notifications.DeleteAllNotificationsRemoteOperation;
+import com.owncloud.android.lib.resources.notifications.models.Action;
+import com.owncloud.android.ui.activity.NotificationsActivity;
+import com.owncloud.android.ui.notifications.NotificationsContract;
+
+public class DeleteAllNotificationsTask extends AsyncTask<Action, Void, Boolean> {
+    private OwnCloudClient client;
+    private NotificationsContract.View notificationsActivity;
+
+    public DeleteAllNotificationsTask(OwnCloudClient client, NotificationsActivity notificationsActivity) {
+        this.client = client;
+        this.notificationsActivity = notificationsActivity;
+    }
+
+    @Override
+    protected Boolean doInBackground(Action... actions) {
+
+        RemoteOperationResult result = new DeleteAllNotificationsRemoteOperation().execute(client);
+
+        return result.isSuccess();
+    }
+
+    @Override
+    protected void onPostExecute(Boolean success) {
+        notificationsActivity.onRemovedAllNotifications(success);
+    }
+}

+ 63 - 0
src/main/java/com/owncloud/android/ui/asynctasks/DeleteNotificationTask.java

@@ -0,0 +1,63 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2019 Tobias Kaminsky
+ * Copyright (C) 2019 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.asynctasks;
+
+import android.os.AsyncTask;
+
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.resources.notifications.DeleteNotificationRemoteOperation;
+import com.owncloud.android.lib.resources.notifications.models.Action;
+import com.owncloud.android.lib.resources.notifications.models.Notification;
+import com.owncloud.android.ui.activity.NotificationsActivity;
+import com.owncloud.android.ui.adapter.NotificationListAdapter;
+import com.owncloud.android.ui.notifications.NotificationsContract;
+
+public class DeleteNotificationTask extends AsyncTask<Action, Void, Boolean> {
+    private Notification notification;
+    private NotificationListAdapter.NotificationViewHolder holder;
+    private OwnCloudClient client;
+    private NotificationsContract.View notificationsActivity;
+
+    public DeleteNotificationTask(OwnCloudClient client, Notification notification,
+                                  NotificationListAdapter.NotificationViewHolder holder,
+                                  NotificationsActivity notificationsActivity) {
+        this.client = client;
+        this.notification = notification;
+        this.holder = holder;
+        this.notificationsActivity = notificationsActivity;
+    }
+
+    @Override
+    protected Boolean doInBackground(Action... actions) {
+
+        RemoteOperationResult result = new DeleteNotificationRemoteOperation(notification.notificationId)
+            .execute(client);
+
+        return result.isSuccess();
+    }
+
+    @Override
+    protected void onPostExecute(Boolean success) {
+        notificationsActivity.onRemovedNotification(success, holder);
+    }
+}

+ 73 - 0
src/main/java/com/owncloud/android/ui/asynctasks/NotificationExecuteActionTask.java

@@ -0,0 +1,73 @@
+package com.owncloud.android.ui.asynctasks;
+
+import android.os.AsyncTask;
+
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.notifications.models.Action;
+import com.owncloud.android.ui.activity.NotificationsActivity;
+import com.owncloud.android.ui.adapter.NotificationListAdapter;
+
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.DeleteMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+
+import java.io.IOException;
+
+public class NotificationExecuteActionTask extends AsyncTask<Action, Void, Boolean> {
+
+    private NotificationListAdapter.NotificationViewHolder holder;
+    private OwnCloudClient client;
+    private NotificationsActivity notificationsActivity;
+
+    public NotificationExecuteActionTask(OwnCloudClient client, NotificationListAdapter.NotificationViewHolder holder,
+                                         NotificationsActivity notificationsActivity) {
+        this.client = client;
+        this.holder = holder;
+        this.notificationsActivity = notificationsActivity;
+    }
+
+    @Override
+    protected Boolean doInBackground(Action... actions) {
+        HttpMethod method;
+        Action action = actions[0];
+
+        switch (action.type) {
+            case "GET":
+                method = new GetMethod(action.link);
+                break;
+
+            case "POST":
+                method = new PostMethod(action.link);
+                break;
+
+            case "DELETE":
+                method = new DeleteMethod(action.link);
+                break;
+
+            default:
+                // do nothing
+                return false;
+        }
+
+        method.setRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE);
+
+        int status;
+        try {
+            status = client.executeMethod(method);
+        } catch (IOException e) {
+            Log_OC.e(this, "Execution of notification action failed: " + e);
+            return false;
+        }
+
+        return status == HttpStatus.SC_OK || status == HttpStatus.SC_ACCEPTED;
+    }
+
+    @Override
+    protected void onPostExecute(Boolean isSuccess) {
+        notificationsActivity.onActionCallback(isSuccess, holder);
+    }
+}

+ 35 - 0
src/main/java/com/owncloud/android/ui/notifications/NotificationsContract.java

@@ -0,0 +1,35 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2019 Tobias Kaminsky
+ * Copyright (C) 2019 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.notifications;
+
+import com.owncloud.android.ui.adapter.NotificationListAdapter;
+
+public interface NotificationsContract {
+
+    interface View {
+        void onRemovedNotification(boolean isSuccess, NotificationListAdapter.NotificationViewHolder holder);
+
+        void onRemovedAllNotifications(boolean isSuccess);
+
+        void onActionCallback(boolean isSuccess, NotificationListAdapter.NotificationViewHolder holder);
+    }
+}

+ 26 - 12
src/main/res/layout/notification_list_item.xml

@@ -5,22 +5,21 @@
   @author Tobias Kaminsky
   @author Tobias Kaminsky
   Copyright (C) 2018 Tobias Kaminsky
   Copyright (C) 2018 Tobias Kaminsky
   Copyright (C) 2018 Nextcloud GmbH.
   Copyright (C) 2018 Nextcloud GmbH.
- 
+
   This program is free software: you can redistribute it and/or modify
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
   (at your option) any later version.
- 
+
   This program is distributed in the hope that it will be useful,
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   GNU General Public License for more details.
   GNU General Public License for more details.
- 
+
   You should have received a copy of the GNU General Public License
   You should have received a copy of the GNU General Public License
   along with this program. If not, see <https://www.gnu.org/licenses/>.
   along with this program. If not, see <https://www.gnu.org/licenses/>.
 -->
 -->
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_height="wrap_content"
@@ -28,7 +27,8 @@
     android:paddingTop="@dimen/standard_padding"
     android:paddingTop="@dimen/standard_padding"
     android:paddingRight="@dimen/standard_padding"
     android:paddingRight="@dimen/standard_padding"
     android:paddingBottom="@dimen/standard_padding"
     android:paddingBottom="@dimen/standard_padding"
-    android:paddingLeft="@dimen/standard_padding">
+    android:paddingLeft="@dimen/standard_padding"
+    tools:ignore="UseCompoundDrawables">
 
 
     <ImageView
     <ImageView
         android:id="@+id/notification_icon"
         android:id="@+id/notification_icon"
@@ -49,13 +49,27 @@
         android:layout_toRightOf="@id/notification_icon"
         android:layout_toRightOf="@id/notification_icon"
         android:layout_toEndOf="@id/notification_icon">
         android:layout_toEndOf="@id/notification_icon">
 
 
-        <TextView
-            android:id="@+id/notification_subject"
+        <LinearLayout
             android:layout_width="match_parent"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            tools:text="@string/placeholder_filename"
-            android:textAppearance="?android:attr/textAppearanceListItem"/>
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/notification_subject"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:ellipsize="end"
+                android:textAppearance="?android:attr/textAppearanceListItem"
+                tools:text="@string/placeholder_filename" />
+
+            <ImageView
+                android:id="@+id/notification_dismiss"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:src="@drawable/ic_close"
+                android:contentDescription="@string/dismiss_notification_description" />
+        </LinearLayout>
 
 
         <TextView
         <TextView
             android:id="@+id/notification_message"
             android:id="@+id/notification_message"

+ 32 - 0
src/main/res/menu/notifications_actions_menu.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Nextcloud Android client application
+
+  @author Tobias Kaminsky
+  Copyright (C) 2019 Tobias Kaminsky
+  Copyright (C) 2019 Nextcloud GmbH.
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program. If not, see <https://www.gnu.org/licenses/>.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:ignore="AppCompatResource">
+
+    <item
+        android:id="@+id/action_empty_notifications"
+        android:contentDescription="@string/action_empty_notifications"
+        android:orderInCategory="1"
+        android:title="@string/action_empty_notifications"
+        app:showAsAction="never" />
+</menu>

+ 3 - 3
src/main/res/menu/trashbin_actions_menu.xml

@@ -5,17 +5,17 @@
   @author Tobias Kaminsky
   @author Tobias Kaminsky
   Copyright (C) 2018 Tobias Kaminsky
   Copyright (C) 2018 Tobias Kaminsky
   Copyright (C) 2018 Nextcloud GmbH.
   Copyright (C) 2018 Nextcloud GmbH.
- 
+
   This program is free software: you can redistribute it and/or modify
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
   (at your option) any later version.
- 
+
   This program is distributed in the hope that it will be useful,
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   GNU General Public License for more details.
   GNU General Public License for more details.
- 
+
   You should have received a copy of the GNU General Public License
   You should have received a copy of the GNU General Public License
   along with this program. If not, see <https://www.gnu.org/licenses/>.
   along with this program. If not, see <https://www.gnu.org/licenses/>.
 -->
 -->

+ 5 - 0
src/main/res/values/strings.xml

@@ -855,4 +855,9 @@
     <string name="upload_local_storage_not_copied">File could not be copied to local storage</string>
     <string name="upload_local_storage_not_copied">File could not be copied to local storage</string>
     <string name="host_not_available">Server not available</string>
     <string name="host_not_available">Server not available</string>
     <string name="delete_entries">Delete entries</string>
     <string name="delete_entries">Delete entries</string>
+    <string name="dismiss_notification_description">Dismiss notification</string>
+    <string name="action_empty_notifications">Clear all notifications</string>
+    <string name="clear_notifications_failed">Failed to clear notifications!</string>
+    <string name="remove_notification_failed">Failed to remove notification!</string>
+    <string name="notification_action_failed">Failed to execute action!</string>
 </resources>
 </resources>