Browse Source

allow to remove notifications, will also be deleted on server

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
tobiasKaminsky 6 years ago
parent
commit
03c33a9207

+ 1 - 0
src/main/AndroidManifest.xml

@@ -116,6 +116,7 @@
             android:configChanges="orientation|screenSize|keyboardHidden" />
         <activity android:name=".ui.activity.SyncedFoldersActivity"/>
         <receiver android:name="com.owncloud.android.jobs.MediaFoldersDetectionJob$NotificationReceiver" />
+        <receiver android:name="com.owncloud.android.jobs.NotificationJob$NotificationReceiver" />
         <activity android:name=".ui.activity.UploadFilesActivity" />
         <activity android:name=".ui.activity.ExternalSiteWebView"
                   android:configChanges="orientation|screenSize|keyboardHidden" />

+ 4 - 0
src/main/java/com/owncloud/android/datamodel/DecryptedPushMessage.java

@@ -32,9 +32,13 @@ import lombok.Setter;
 @Setter
 @NoArgsConstructor
 @AllArgsConstructor
+/*
+ * Push data from server, https://github.com/nextcloud/notifications/blob/master/docs/push-v2.md#encrypted-subject-data
+ */
 public class DecryptedPushMessage {
     public String app;
     public String type;
     public String subject;
     public String id;
+    public int nid;
 }

+ 87 - 23
src/main/java/com/owncloud/android/jobs/NotificationJob.java

@@ -21,8 +21,12 @@
 package com.owncloud.android.jobs;
 
 import android.accounts.Account;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Activity;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.BitmapFactory;
@@ -35,23 +39,31 @@ import com.evernote.android.job.Job;
 import com.evernote.android.job.util.support.PersistableBundleCompat;
 import com.google.gson.Gson;
 import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.datamodel.DecryptedPushMessage;
 import com.owncloud.android.datamodel.SignatureVerification;
+import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
 import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.notifications.DeleteNotificationRemoteOperation;
 import com.owncloud.android.ui.activity.NotificationsActivity;
 import com.owncloud.android.ui.notifications.NotificationUtils;
 import com.owncloud.android.utils.PushUtils;
 import com.owncloud.android.utils.ThemeUtils;
 
+import java.io.IOException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
+import java.util.Random;
 
 import javax.crypto.Cipher;
 import javax.crypto.NoSuchPaddingException;
 
 import androidx.annotation.NonNull;
 import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
 
 public class NotificationJob extends Job {
     public static final String TAG = "NotificationJob";
@@ -59,6 +71,10 @@ public class NotificationJob extends Job {
     public static final String KEY_NOTIFICATION_SUBJECT = "subject";
     public static final String KEY_NOTIFICATION_SIGNATURE = "signature";
     public static final String KEY_NOTIFICATION_ACCOUNT = "KEY_NOTIFICATION_ACCOUNT";
+    private static final String PUSH_NOTIFICATION_ID = "PUSH_NOTIFICATION_ID";
+    private static final String NUMERIC_NOTIFICATION_ID = "NUMERIC_NOTIFICATION_ID";
+
+    private Random randomId = new Random();
 
     @NonNull
     @Override
@@ -77,7 +93,8 @@ public class NotificationJob extends Job {
 
                 try {
                     SignatureVerification signatureVerification = PushUtils.verifySignature(context,
-                            base64DecodedSignature, base64DecodedSubject);
+                                                                                            base64DecodedSignature,
+                                                                                            base64DecodedSubject);
 
                     if (signatureVerification.isSignatureValid()) {
                         Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");
@@ -86,16 +103,16 @@ public class NotificationJob extends Job {
 
                         Gson gson = new Gson();
                         DecryptedPushMessage decryptedPushMessage = gson.fromJson(new String(decryptedSubject),
-                                DecryptedPushMessage.class);
+                                                                                  DecryptedPushMessage.class);
 
                         // We ignore Spreed messages for now
                         if (!"spreed".equals(decryptedPushMessage.getApp())) {
-                            sendNotification(decryptedPushMessage.getSubject(), signatureVerification.getAccount());
+                            sendNotification(decryptedPushMessage, signatureVerification.getAccount());
                         }
                     }
                 } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e1) {
                     Log.d(TAG, "Error decrypting message " + e1.getClass().getName()
-                            + " " + e1.getLocalizedMessage());
+                        + " " + e1.getLocalizedMessage());
                 }
             } catch (Exception exception) {
                 Log.d(TAG, "Something went very wrong" + exception.getLocalizedMessage());
@@ -104,36 +121,83 @@ public class NotificationJob extends Job {
         return Result.SUCCESS;
     }
 
-    private void sendNotification(String contentTitle, Account account) {
+    private void sendNotification(DecryptedPushMessage pushMessage, Account account) {
         Context context = getContext();
         Intent intent = new Intent(getContext(), NotificationsActivity.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
         intent.putExtra(KEY_NOTIFICATION_ACCOUNT, account.name);
         PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+        int pushNotificationId = randomId.nextInt();
+
+        NotificationCompat.Builder notificationBuilder =
+            new NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_PUSH)
+                .setSmallIcon(R.drawable.notification_icon)
+                .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.notification_icon))
+                .setColor(ThemeUtils.primaryColor(account, false, context))
+                .setShowWhen(true)
+                .setSubText(account.name)
+                .setContentTitle(pushMessage.getSubject())
+                .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
+                .setAutoCancel(true)
+                .setContentIntent(pendingIntent);
+
+        Intent disableDetection = new Intent(context, NotificationJob.NotificationReceiver.class);
+        disableDetection.putExtra(NUMERIC_NOTIFICATION_ID, pushMessage.getNid());
+        disableDetection.putExtra(PUSH_NOTIFICATION_ID, pushNotificationId);
+        disableDetection.putExtra(KEY_NOTIFICATION_ACCOUNT, account.name);
+
+        PendingIntent disableIntent = PendingIntent.getBroadcast(context, pushNotificationId, disableDetection,
+                                                                 PendingIntent.FLAG_CANCEL_CURRENT);
+
+        notificationBuilder.addAction(new NotificationCompat.Action(R.drawable.ic_close,
+                                                                    context.getString(R.string.remove_push_notification), disableIntent));
+
+        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
+        notificationManager.notify(pushNotificationId, notificationBuilder.build());
+    }
+
+    public static class NotificationReceiver extends BroadcastReceiver {
 
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            int numericNotificationId = intent.getIntExtra(NUMERIC_NOTIFICATION_ID, 0);
+            int pushNotificationId = intent.getIntExtra(PUSH_NOTIFICATION_ID, 0);
+            String accountName = intent.getStringExtra(NotificationJob.KEY_NOTIFICATION_ACCOUNT);
+
+            if (numericNotificationId != 0) {
+                new Thread(() -> {
+                    try {
+                        Account currentAccount = AccountUtils.getOwnCloudAccountByName(context, accountName);
+
+                        if (currentAccount == null) {
+                            Log_OC.e(this, "Account may not be null");
+                            return;
+                        }
 
-        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
-            .setSmallIcon(R.drawable.notification_icon)
-            .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.notification_icon))
-            .setColor(ThemeUtils.primaryColor(account, false, context))
-            .setShowWhen(true)
-            .setSubText(account.name)
-            .setContentTitle(contentTitle)
-            .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
-            .setAutoCancel(true)
-            .setContentIntent(pendingIntent);
+                        OwnCloudAccount ocAccount = new OwnCloudAccount(currentAccount, context);
+                        OwnCloudClient client = OwnCloudClientManagerFactory.getDefaultSingleton()
+                            .getClientFor(ocAccount, context);
+                        client.setOwnCloudVersion(AccountUtils.getServerVersion(currentAccount));
 
-        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
-            notificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_PUSH);
+                        new DeleteNotificationRemoteOperation(numericNotificationId).execute(client);
+
+                        cancel(context, pushNotificationId);
+
+                    } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException |
+                        IOException | OperationCanceledException | AuthenticatorException e) {
+                        Log_OC.e(TAG, "Error initializing client", e);
+                    }
+                }).start();
+            }
         }
 
-        NotificationManager notificationManager = (NotificationManager)
-            context.getSystemService(Context.NOTIFICATION_SERVICE);
+        private void cancel(Context context, int notificationId) {
+            NotificationManager notificationManager = (NotificationManager) context.getSystemService(
+                Activity.NOTIFICATION_SERVICE);
 
-        if (notificationManager != null) {
-            notificationManager.notify(0, notificationBuilder.build());
-        } else {
-            Log_OC.e(this, "Cannot notify about received push notification: NotificationManager is null");
+            if (notificationManager != null) {
+                notificationManager.cancel(notificationId);
+            }
         }
     }
 }

+ 4 - 3
src/main/res/values/strings.xml

@@ -857,7 +857,8 @@
     <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>
+    <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>
+    <string name="remove_push_notification">Remove</string>
 </resources>