Compare commits
4 Commits
5f679df5cf
...
1e00105d87
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e00105d87 | ||
|
|
855deaa48a | ||
|
|
c1b287986d | ||
|
|
0a38409de4 |
@@ -2,6 +2,7 @@ package im.rosetta.service.dispatch;
|
|||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
@@ -9,14 +10,15 @@ import java.util.concurrent.Executors;
|
|||||||
import com.google.auth.oauth2.GoogleCredentials;
|
import com.google.auth.oauth2.GoogleCredentials;
|
||||||
import com.google.firebase.FirebaseApp;
|
import com.google.firebase.FirebaseApp;
|
||||||
import com.google.firebase.FirebaseOptions;
|
import com.google.firebase.FirebaseOptions;
|
||||||
|
import com.google.firebase.messaging.AndroidConfig;
|
||||||
import com.google.firebase.messaging.ApnsConfig;
|
import com.google.firebase.messaging.ApnsConfig;
|
||||||
import com.google.firebase.messaging.Aps;
|
import com.google.firebase.messaging.Aps;
|
||||||
import com.google.firebase.messaging.ApsAlert;
|
|
||||||
import com.google.firebase.messaging.FirebaseMessaging;
|
import com.google.firebase.messaging.FirebaseMessaging;
|
||||||
import com.google.firebase.messaging.Message;
|
import com.google.firebase.messaging.Message;
|
||||||
import com.google.firebase.messaging.Notification;
|
import com.google.firebase.messaging.Notification;
|
||||||
|
|
||||||
import im.rosetta.database.repository.UserRepository;
|
import im.rosetta.database.repository.UserRepository;
|
||||||
|
import im.rosetta.service.dispatch.runtime.PushType;
|
||||||
import im.rosetta.service.services.UserService;
|
import im.rosetta.service.services.UserService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,7 +56,54 @@ public class FirebaseDispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPushNotification(String publicKey, String title, String messageText, String senderPublicKey) {
|
private Message buildMessage(String token, HashMap<String, String> data) {
|
||||||
|
String type = data.get("type");
|
||||||
|
if(type == null){
|
||||||
|
throw new IllegalArgumentException("Push notification type is required in data");
|
||||||
|
}
|
||||||
|
ApnsConfig.Builder apnsConfig = ApnsConfig.builder();
|
||||||
|
AndroidConfig.Builder androidConfig = AndroidConfig.builder();
|
||||||
|
Message.Builder messageBuilder = Message.builder()
|
||||||
|
.setToken(token)
|
||||||
|
.putAllData(data);
|
||||||
|
switch(type) {
|
||||||
|
case PushType.READ:
|
||||||
|
/**
|
||||||
|
* Тихий тип уведомления для очистки отправленных уведомлений на устройстве,
|
||||||
|
* не должен отображаться пользователю, поэтому не задаем звук и ставим contentAvailable для iOS и high priority для Android
|
||||||
|
*/
|
||||||
|
apnsConfig.setAps(Aps.builder().setContentAvailable(true).setSound("default").build());
|
||||||
|
androidConfig.setPriority(AndroidConfig.Priority.HIGH);
|
||||||
|
messageBuilder.setApnsConfig(apnsConfig.build());
|
||||||
|
messageBuilder.setAndroidConfig(androidConfig.build());
|
||||||
|
break;
|
||||||
|
case PushType.PERSONAL_MESSAGE:
|
||||||
|
case PushType.GROUP_MESSAGE:
|
||||||
|
/**
|
||||||
|
* Уведомление о новом сообщении, должно отображаться пользователю, поэтому задаем звук и high priority для Android
|
||||||
|
*/
|
||||||
|
String body = type == PushType.PERSONAL_MESSAGE ? "New message" : "New group message";
|
||||||
|
apnsConfig.setAps(Aps.builder().setSound("default").setMutableContent(true).build());
|
||||||
|
androidConfig.setPriority(AndroidConfig.Priority.HIGH);
|
||||||
|
messageBuilder.setApnsConfig(apnsConfig.build());
|
||||||
|
messageBuilder.setNotification(Notification.builder().setTitle(
|
||||||
|
data.getOrDefault("title", "Rosetta")
|
||||||
|
).setBody(body).build());
|
||||||
|
messageBuilder.setAndroidConfig(androidConfig.build());
|
||||||
|
break;
|
||||||
|
case PushType.CALL:
|
||||||
|
/**
|
||||||
|
* Звонок для андроид используем high priority, чтобы уведомление доставлялось даже если устройство в режиме Doze,
|
||||||
|
* для iOS используем VoIP уведомление, которое доставляется даже если приложение убито
|
||||||
|
*/
|
||||||
|
androidConfig.setPriority(AndroidConfig.Priority.HIGH);
|
||||||
|
messageBuilder.setAndroidConfig(androidConfig.build());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return messageBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPushNotification(String publicKey, HashMap<String, String> data) {
|
||||||
executor.submit(() -> {
|
executor.submit(() -> {
|
||||||
try {
|
try {
|
||||||
List<String> tokens = userService.getNotificationsTokens(publicKey);
|
List<String> tokens = userService.getNotificationsTokens(publicKey);
|
||||||
@@ -64,25 +113,7 @@ public class FirebaseDispatcher {
|
|||||||
|
|
||||||
for (String token : tokens) {
|
for (String token : tokens) {
|
||||||
try {
|
try {
|
||||||
Message message = Message.builder()
|
Message message = this.buildMessage(token, data);
|
||||||
.setNotification(Notification.builder()
|
|
||||||
.setTitle(title)
|
|
||||||
.setBody(messageText)
|
|
||||||
.build())
|
|
||||||
.setApnsConfig(ApnsConfig.builder()
|
|
||||||
.setAps(Aps.builder()
|
|
||||||
.setMutableContent(true)
|
|
||||||
.setSound("default")
|
|
||||||
.setAlert(ApsAlert.builder()
|
|
||||||
.setTitle(title)
|
|
||||||
.setBody(messageText)
|
|
||||||
.build())
|
|
||||||
.build())
|
|
||||||
.build())
|
|
||||||
.putData("sender_public_key", senderPublicKey)
|
|
||||||
.setToken(token)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
FirebaseMessaging.getInstance().send(message);
|
FirebaseMessaging.getInstance().send(message);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -94,45 +125,20 @@ public class FirebaseDispatcher {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Отправляет push-уведомление пользователю с данным публичным ключом (асинхронно)
|
|
||||||
* @param publicKey публичный ключ пользователя
|
|
||||||
* @param title заголовок уведомления
|
|
||||||
* @param messageText текст уведомления
|
|
||||||
*/
|
|
||||||
public void sendPushNotification(String publicKey, String title, String messageText) {
|
|
||||||
sendPushNotification(publicKey, title, messageText, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Отправляет push-уведомление нескольким пользователям (асинхронно)
|
* Отправляет push-уведомление нескольким пользователям (асинхронно)
|
||||||
* @param publicKeys список публичных ключей пользователей
|
* @param publicKeys список публичных ключей пользователей
|
||||||
* @param title заголовок уведомления
|
* @param data данные уведомления
|
||||||
* @param messageText текст уведомления
|
|
||||||
*/
|
*/
|
||||||
public void sendPushNotification(List<String> publicKeys, String title, String messageText, String senderPublicKey) {
|
public void sendPushNotification(List<String> publicKeys, HashMap<String, String> data) {
|
||||||
executor.submit(() -> {
|
executor.submit(() -> {
|
||||||
for (String publicKey : publicKeys) {
|
for (String publicKey : publicKeys) {
|
||||||
sendPushNotificationSync(publicKey, title, messageText, senderPublicKey);
|
sendPushNotificationSync(publicKey, data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void sendPushNotificationSync(String publicKey, HashMap<String, String> data) {
|
||||||
* Отправляет push-уведомление нескольким пользователям (асинхронно)
|
|
||||||
* @param publicKeys список публичных ключей пользователей
|
|
||||||
* @param title заголовок уведомления
|
|
||||||
* @param messageText текст уведомления
|
|
||||||
*/
|
|
||||||
public void sendPushNotification(List<String> publicKeys, String title, String messageText) {
|
|
||||||
executor.submit(() -> {
|
|
||||||
for (String publicKey : publicKeys) {
|
|
||||||
sendPushNotificationSync(publicKey, title, messageText, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendPushNotificationSync(String publicKey, String title, String messageText, String senderPublicKey) {
|
|
||||||
try {
|
try {
|
||||||
List<String> tokens = userService.getNotificationsTokens(publicKey);
|
List<String> tokens = userService.getNotificationsTokens(publicKey);
|
||||||
if (tokens == null || tokens.isEmpty()) {
|
if (tokens == null || tokens.isEmpty()) {
|
||||||
@@ -141,24 +147,7 @@ public class FirebaseDispatcher {
|
|||||||
|
|
||||||
for (String token : tokens) {
|
for (String token : tokens) {
|
||||||
try {
|
try {
|
||||||
Message message = Message.builder()
|
Message message = this.buildMessage(token, data);
|
||||||
.setNotification(Notification.builder()
|
|
||||||
.setTitle(title)
|
|
||||||
.setBody(messageText)
|
|
||||||
.build())
|
|
||||||
.setApnsConfig(ApnsConfig.builder()
|
|
||||||
.setAps(Aps.builder()
|
|
||||||
.setMutableContent(true)
|
|
||||||
.setSound("default")
|
|
||||||
.setAlert(ApsAlert.builder()
|
|
||||||
.setTitle(title)
|
|
||||||
.setBody(messageText)
|
|
||||||
.build())
|
|
||||||
.build())
|
|
||||||
.build())
|
|
||||||
.putData("sender_public_key", senderPublicKey != null ? senderPublicKey : "")
|
|
||||||
.setToken(token)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
FirebaseMessaging.getInstance().send(message);
|
FirebaseMessaging.getInstance().send(message);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package im.rosetta.service.dispatch;
|
package im.rosetta.service.dispatch;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import im.rosetta.client.ClientManager;
|
import im.rosetta.client.ClientManager;
|
||||||
@@ -11,6 +12,7 @@ import im.rosetta.database.repository.UserRepository;
|
|||||||
import im.rosetta.packet.Packet11Typeing;
|
import im.rosetta.packet.Packet11Typeing;
|
||||||
import im.rosetta.packet.Packet7Read;
|
import im.rosetta.packet.Packet7Read;
|
||||||
import im.rosetta.packet.base.PacketBaseDialog;
|
import im.rosetta.packet.base.PacketBaseDialog;
|
||||||
|
import im.rosetta.service.dispatch.runtime.PushType;
|
||||||
import im.rosetta.service.services.BufferService;
|
import im.rosetta.service.services.BufferService;
|
||||||
import im.rosetta.service.services.UserService;
|
import im.rosetta.service.services.UserService;
|
||||||
import io.orprotocol.ProtocolException;
|
import io.orprotocol.ProtocolException;
|
||||||
@@ -98,14 +100,26 @@ public class MessageDispatcher {
|
|||||||
}
|
}
|
||||||
if(packet instanceof Packet7Read){
|
if(packet instanceof Packet7Read){
|
||||||
/**
|
/**
|
||||||
* Если это пакет прочтения, то не отправляем пуш уведомление, так как это может привести к спаму пушами при чтении сообщений
|
* Если это пакет прочтения, то отправляем тихий пуш, что диалог прочитан, отправляем тому, кто читает диалог, чтобы
|
||||||
|
* клиент мог очистить пуши для этого диалога
|
||||||
*/
|
*/
|
||||||
|
this.firebaseDispatcher.sendPushNotification(fromPublicKey, new HashMap<>(){
|
||||||
|
{
|
||||||
|
put("type", PushType.READ);
|
||||||
|
put("dialog", toPublicKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Отправляем PUSH уведомление
|
* Отправляем PUSH уведомление
|
||||||
*/
|
*/
|
||||||
this.firebaseDispatcher.sendPushNotification(groupMembersPublicKeys, "Rosetta", "New message in group", toPublicKey.replace("#group:", ""));
|
this.firebaseDispatcher.sendPushNotification(groupMembersPublicKeys, new HashMap<>(){
|
||||||
|
{
|
||||||
|
put("type", PushType.GROUP_MESSAGE);
|
||||||
|
put("dialog", toPublicKey.replace("#group:", ""));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -156,15 +170,28 @@ public class MessageDispatcher {
|
|||||||
}
|
}
|
||||||
if(packet instanceof Packet7Read){
|
if(packet instanceof Packet7Read){
|
||||||
/**
|
/**
|
||||||
* Если это пакет прочтения, то не отправляем пуш уведомление,
|
* Если это пакет прочтения, то отправляем тихий пуш, что диалог прочитан, отправляем тому, кто читает диалог, чтобы
|
||||||
* так как это может привести к спаму пушами при чтении сообщений
|
* клиент мог очистить пуши для этого диалога
|
||||||
*/
|
*/
|
||||||
|
this.firebaseDispatcher.sendPushNotification(fromPublicKey, new HashMap<>(){
|
||||||
|
{
|
||||||
|
put("type", PushType.READ);
|
||||||
|
put("dialog", toPublicKey);
|
||||||
|
put("title", user.getTitle());
|
||||||
|
}
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Отправляем PUSH уведомление получателю
|
* Отправляем PUSH уведомление получателю
|
||||||
*/
|
*/
|
||||||
this.firebaseDispatcher.sendPushNotification(toPublicKey, user.getTitle(), "New message", fromPublicKey);
|
this.firebaseDispatcher.sendPushNotification(toPublicKey, new HashMap<>(){
|
||||||
|
{
|
||||||
|
put("type", PushType.PERSONAL_MESSAGE);
|
||||||
|
put("dialog", fromPublicKey);
|
||||||
|
put("title", user.getTitle());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package im.rosetta.service.dispatch.runtime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Типы PUSH уведомлений, которые отправляются клиентам при получении новых сообщений, звонков и т.д.
|
||||||
|
*/
|
||||||
|
public class PushType {
|
||||||
|
/**
|
||||||
|
* Новое личное сообщение
|
||||||
|
*/
|
||||||
|
public static final String PERSONAL_MESSAGE = "personal_message";
|
||||||
|
/**
|
||||||
|
* Новое групповое сообщение
|
||||||
|
*/
|
||||||
|
public static final String GROUP_MESSAGE = "group_message";
|
||||||
|
/**
|
||||||
|
* Входящий звонок
|
||||||
|
*/
|
||||||
|
public static final String CALL = "call";
|
||||||
|
/**
|
||||||
|
* Прочтение сообщения для очистки отправленных уведомлений
|
||||||
|
*/
|
||||||
|
public static final String READ = "read";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user