mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-25 14:35:40 +03:00
Initial work on grouping messages
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
2fd7b56a10
commit
90d494e3ea
4 changed files with 186 additions and 16 deletions
|
@ -34,17 +34,30 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.app.Person;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.work.Data;
|
||||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
import autodagger.AutoInjector;
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
import com.facebook.common.executors.UiThreadImmediateExecutorService;
|
||||
import com.facebook.common.references.CloseableReference;
|
||||
import com.facebook.datasource.DataSource;
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.imagepipeline.core.ImagePipeline;
|
||||
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
|
||||
import com.facebook.imagepipeline.image.CloseableImage;
|
||||
import com.facebook.imagepipeline.postprocessors.RoundAsCirclePostprocessor;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.activities.MagicCallActivity;
|
||||
import com.nextcloud.talk.activities.MainActivity;
|
||||
|
@ -57,12 +70,10 @@ import com.nextcloud.talk.models.database.UserEntity;
|
|||
import com.nextcloud.talk.models.json.chat.ChatUtils;
|
||||
import com.nextcloud.talk.models.json.notifications.NotificationOverall;
|
||||
import com.nextcloud.talk.models.json.push.DecryptedPushMessage;
|
||||
import com.nextcloud.talk.models.json.push.NotificationUser;
|
||||
import com.nextcloud.talk.models.json.rooms.Conversation;
|
||||
import com.nextcloud.talk.models.json.rooms.RoomOverall;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DoNotDisturbUtils;
|
||||
import com.nextcloud.talk.utils.NotificationUtils;
|
||||
import com.nextcloud.talk.utils.PushUtils;
|
||||
import com.nextcloud.talk.utils.*;
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
||||
import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils;
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
||||
|
@ -198,12 +209,16 @@ public class NotificationWorker extends Worker {
|
|||
HashMap<String, String> callHashMap = subjectRichParameters.get("call");
|
||||
HashMap<String, String> userHashMap = subjectRichParameters.get("user");
|
||||
|
||||
if (callHashMap != null && callHashMap.size() > 0 && callHashMap.containsKey("call-type")) {
|
||||
conversationType = callHashMap.get("call-type");
|
||||
|
||||
if ("one2one".equals(conversationType)) {
|
||||
decryptedPushMessage.setSubject(userHashMap.get("name"));
|
||||
if (callHashMap != null && callHashMap.size() > 0 && callHashMap.containsKey("name")) {
|
||||
decryptedPushMessage.setSubject(callHashMap.get("name"));
|
||||
}
|
||||
|
||||
if (userHashMap != null && !userHashMap.isEmpty()) {
|
||||
NotificationUser notificationUser = new NotificationUser();
|
||||
notificationUser.setId(userHashMap.get("id"));
|
||||
notificationUser.setType(userHashMap.get("type"));
|
||||
notificationUser.setName(userHashMap.get("name"));
|
||||
decryptedPushMessage.setNotificationUser(notificationUser);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +241,6 @@ public class NotificationWorker extends Worker {
|
|||
Bitmap largeIcon = null;
|
||||
String category;
|
||||
int priority = Notification.PRIORITY_HIGH;
|
||||
Uri soundUri;
|
||||
|
||||
smallIcon = R.drawable.ic_logo;
|
||||
|
||||
|
@ -263,7 +277,6 @@ public class NotificationWorker extends Worker {
|
|||
PendingIntent pendingIntent = PendingIntent.getActivity(context,
|
||||
0, intent, 0);
|
||||
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
Uri uri = Uri.parse(signatureVerification.getUserEntity().getBaseUrl());
|
||||
String baseUrl = uri.getHost();
|
||||
|
||||
|
@ -327,7 +340,6 @@ public class NotificationWorker extends Worker {
|
|||
|
||||
notificationBuilder.setContentIntent(pendingIntent);
|
||||
|
||||
String stringForCrc = String.valueOf(System.currentTimeMillis());
|
||||
|
||||
CRC32 crc32 = new CRC32();
|
||||
|
||||
|
@ -335,10 +347,101 @@ public class NotificationWorker extends Worker {
|
|||
crc32.update(groupName.getBytes());
|
||||
notificationBuilder.setGroup(Long.toString(crc32.getValue()));
|
||||
|
||||
|
||||
// notificationId
|
||||
crc32 = new CRC32();
|
||||
String stringForCrc = String.valueOf(System.currentTimeMillis());
|
||||
crc32.update(stringForCrc.getBytes());
|
||||
|
||||
StatusBarNotification activeStatusBarNotification =
|
||||
NotificationUtils.findNotificationForRoom(context,
|
||||
signatureVerification.getUserEntity(), decryptedPushMessage.getId());
|
||||
|
||||
int notificationId;
|
||||
|
||||
if (activeStatusBarNotification != null) {
|
||||
notificationId = activeStatusBarNotification.getId();
|
||||
} else {
|
||||
notificationId = (int) crc32.getValue();
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
|
||||
NotificationCompat.MessagingStyle style = null;
|
||||
if (activeStatusBarNotification != null) {
|
||||
Notification activeNotification = activeStatusBarNotification.getNotification();
|
||||
style = NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(activeNotification);
|
||||
}
|
||||
|
||||
Person.Builder person =
|
||||
new Person.Builder().setKey(signatureVerification.getUserEntity().getId() + "@" + decryptedPushMessage.getNotificationUser().getId()).setName(decryptedPushMessage.getNotificationUser().getName()).setBot(decryptedPushMessage.getNotificationUser().getType().equals("bot"));
|
||||
|
||||
notificationBuilder.setOnlyAlertOnce(true);
|
||||
|
||||
if (decryptedPushMessage.getNotificationUser().getType().equals("user")) {
|
||||
ImageRequest imageRequest =
|
||||
DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(signatureVerification.getUserEntity().getBaseUrl(), decryptedPushMessage.getNotificationUser().getId(), R.dimen.avatar_size), null);
|
||||
ImagePipeline imagePipeline = Fresco.getImagePipeline();
|
||||
DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, context);
|
||||
|
||||
NotificationCompat.MessagingStyle finalStyle = style;
|
||||
dataSource.subscribe(
|
||||
new BaseBitmapDataSubscriber() {
|
||||
@Override
|
||||
protected void onNewResultImpl(Bitmap bitmap) {
|
||||
if (bitmap != null) {
|
||||
new RoundAsCirclePostprocessor(true).process(bitmap);
|
||||
person.setIcon(IconCompat.createWithBitmap(bitmap));
|
||||
notificationBuilder.setStyle(getStyle(person.build(),
|
||||
finalStyle));
|
||||
sendNotificationWithId(notificationId, notificationBuilder.build());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
|
||||
notificationBuilder.setStyle(getStyle(person.build(), finalStyle));
|
||||
sendNotificationWithId(notificationId, notificationBuilder.build());
|
||||
}
|
||||
},
|
||||
UiThreadImmediateExecutorService.getInstance());
|
||||
} else {
|
||||
notificationBuilder.setStyle(getStyle(person.build(), style));
|
||||
sendNotificationWithId(notificationId, notificationBuilder.build());
|
||||
}
|
||||
} else {
|
||||
sendNotificationWithId(notificationId, notificationBuilder.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private NotificationCompat.MessagingStyle getStyle(Person person, @Nullable NotificationCompat.MessagingStyle style) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
NotificationCompat.MessagingStyle newStyle =
|
||||
new NotificationCompat.MessagingStyle(person);
|
||||
|
||||
newStyle.setConversationTitle(decryptedPushMessage.getSubject());
|
||||
newStyle.setGroupConversation(!conversationType.equals("one2one"));
|
||||
|
||||
if (style != null) {
|
||||
style.getMessages().forEach(message -> newStyle.addMessage(new NotificationCompat.MessagingStyle.Message(message.getText(), message.getTimestamp(), message.getPerson())));
|
||||
}
|
||||
|
||||
newStyle.addMessage(decryptedPushMessage.getText(), System.currentTimeMillis(), person);
|
||||
return newStyle;
|
||||
}
|
||||
|
||||
// we'll never come here
|
||||
return style;
|
||||
}
|
||||
|
||||
private void sendNotificationWithId(int notificationId, Notification notification) {
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
notificationManager.notify(notificationId, notification);
|
||||
|
||||
String ringtonePreferencesString;
|
||||
Uri soundUri;
|
||||
|
||||
ringtonePreferencesString = appPreferences.getMessageRingtoneUri();
|
||||
if (TextUtils.isEmpty(ringtonePreferencesString)) {
|
||||
soundUri = Uri.parse("android.resource://" + context.getPackageName() +
|
||||
|
@ -354,9 +457,6 @@ public class NotificationWorker extends Worker {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
notificationManager.notify((int) crc32.getValue(), notificationBuilder.build());
|
||||
|
||||
if (soundUri != null & !ApplicationWideCurrentRoomHolder.getInstance().isInCall() &&
|
||||
DoNotDisturbUtils.shouldPlaySound()) {
|
||||
AudioAttributes.Builder audioAttributesBuilder = new AudioAttributes.Builder().setContentType
|
||||
|
@ -380,7 +480,6 @@ public class NotificationWorker extends Worker {
|
|||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to set data source");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -51,6 +51,9 @@ public class DecryptedPushMessage {
|
|||
@JsonField(name = "delete-all")
|
||||
boolean deleteAll;
|
||||
|
||||
@JsonIgnore
|
||||
NotificationUser notificationUser;
|
||||
|
||||
@JsonIgnore
|
||||
String text;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.models.json.push;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import lombok.Data;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
@Data
|
||||
@Parcel
|
||||
@JsonObject
|
||||
public class NotificationUser {
|
||||
@JsonField(name = "type")
|
||||
String type;
|
||||
|
||||
@JsonField(name = "id")
|
||||
String id;
|
||||
|
||||
@JsonField(name = "name")
|
||||
String name;
|
||||
}
|
|
@ -130,6 +130,34 @@ public class NotificationUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static StatusBarNotification findNotificationForRoom(Context context,
|
||||
UserEntity conversationUser,
|
||||
String roomTokenOrId) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M && conversationUser.getId() != -1 &&
|
||||
context != null) {
|
||||
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
if (notificationManager != null) {
|
||||
StatusBarNotification[] statusBarNotifications = notificationManager.getActiveNotifications();
|
||||
Notification notification;
|
||||
for (StatusBarNotification statusBarNotification : statusBarNotifications) {
|
||||
notification = statusBarNotification.getNotification();
|
||||
|
||||
if (notification != null && !notification.extras.isEmpty()) {
|
||||
if (conversationUser.getId() == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) &&
|
||||
roomTokenOrId.equals(statusBarNotification.getNotification().extras.getString(BundleKeys.KEY_ROOM_TOKEN))) {
|
||||
return statusBarNotification;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void cancelExistingNotificationsForRoom(Context context, UserEntity conversationUser,
|
||||
String roomTokenOrId) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M && conversationUser.getId() != -1 &&
|
||||
|
|
Loading…
Reference in a new issue