mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-20 12:09:45 +01:00
Move notification worker to Kotlin & Coin
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
5141abeecc
commit
a0d932e946
@ -1,684 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2018 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.jobs;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
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.emoji.text.EmojiCompat;
|
||||
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;
|
||||
import com.nextcloud.talk.api.NcApi;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.models.RingtoneSettings;
|
||||
import com.nextcloud.talk.models.SignatureVerification;
|
||||
import com.nextcloud.talk.models.database.ArbitraryStorageEntity;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.chat.ChatUtils;
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation;
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall;
|
||||
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.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
import com.nextcloud.talk.utils.DoNotDisturbUtils;
|
||||
import com.nextcloud.talk.utils.NotificationUtils;
|
||||
import com.nextcloud.talk.utils.PushUtils;
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
||||
import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils;
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder;
|
||||
import io.reactivex.Observer;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import java.io.IOException;
|
||||
import java.net.CookieManager;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.zip.CRC32;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.inject.Inject;
|
||||
import okhttp3.JavaNetCookieJar;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.parceler.Parcels;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class NotificationWorker extends Worker {
|
||||
public static final String TAG = "NotificationWorker";
|
||||
|
||||
@Inject
|
||||
AppPreferences appPreferences;
|
||||
|
||||
@Inject
|
||||
ArbitraryStorageUtils arbitraryStorageUtils;
|
||||
|
||||
@Inject
|
||||
Retrofit retrofit;
|
||||
|
||||
@Inject
|
||||
OkHttpClient okHttpClient;
|
||||
|
||||
private NcApi ncApi;
|
||||
|
||||
private DecryptedPushMessage decryptedPushMessage;
|
||||
private Context context;
|
||||
private SignatureVerification signatureVerification;
|
||||
private String conversationType = "one2one";
|
||||
|
||||
private String credentials;
|
||||
private boolean muteCall = false;
|
||||
private boolean importantConversation = false;
|
||||
|
||||
public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||
super(context, workerParams);
|
||||
}
|
||||
|
||||
private void showNotificationForCallWithNoPing(Intent intent) {
|
||||
UserEntity userEntity = signatureVerification.getUserEntity();
|
||||
|
||||
ArbitraryStorageEntity arbitraryStorageEntity;
|
||||
|
||||
if ((arbitraryStorageEntity = arbitraryStorageUtils.getStorageSetting(userEntity.getId(),
|
||||
"mute_calls", intent.getExtras().getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN())))
|
||||
!= null) {
|
||||
muteCall = Boolean.parseBoolean(arbitraryStorageEntity.getValue());
|
||||
}
|
||||
|
||||
if ((arbitraryStorageEntity = arbitraryStorageUtils.getStorageSetting(userEntity.getId(),
|
||||
"important_conversation",
|
||||
intent.getExtras().getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()))) != null) {
|
||||
importantConversation = Boolean.parseBoolean(arbitraryStorageEntity.getValue());
|
||||
}
|
||||
|
||||
if (DoNotDisturbUtils.INSTANCE.isDnDActive()) {
|
||||
if (!DoNotDisturbUtils.INSTANCE.isInDoNotDisturbWithPriority()
|
||||
|| !importantConversation
|
||||
|| muteCall) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ncApi.getRoom(credentials, ApiUtils.getRoom(userEntity.getBaseUrl(),
|
||||
intent.getExtras().getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN())))
|
||||
.blockingSubscribe(new Observer<RoomOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(RoomOverall roomOverall) {
|
||||
Conversation conversation = roomOverall.getOcs().getData();
|
||||
|
||||
intent.putExtra(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation));
|
||||
if (conversation.getType()
|
||||
.equals(Conversation.ConversationType.ONE_TO_ONE_CONVERSATION) ||
|
||||
(!TextUtils.isEmpty(conversation.getObjectType()) && "share:password".equals
|
||||
(conversation.getObjectType()))) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
if (conversation.getType().equals(Conversation.ConversationType.GROUP_CONVERSATION)) {
|
||||
conversationType = "group";
|
||||
} else {
|
||||
conversationType = "public";
|
||||
}
|
||||
if (decryptedPushMessage.getNotificationId() != Long.MIN_VALUE) {
|
||||
showNotificationWithObjectData(intent);
|
||||
} else {
|
||||
showNotification(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showNotificationWithObjectData(Intent intent) {
|
||||
UserEntity userEntity = signatureVerification.getUserEntity();
|
||||
ncApi.getNotification(credentials, ApiUtils.getUrlForNotificationWithId(userEntity.getBaseUrl(),
|
||||
Long.toString(decryptedPushMessage.getNotificationId())))
|
||||
.blockingSubscribe(new Observer<NotificationOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(NotificationOverall notificationOverall) {
|
||||
com.nextcloud.talk.models.json.notifications.Notification notification =
|
||||
notificationOverall.getOcs().getNotification();
|
||||
|
||||
if (notification.getMessageRichParameters() != null &&
|
||||
notification.getMessageRichParameters().size() > 0) {
|
||||
decryptedPushMessage.setText(ChatUtils.getParsedMessage(notification.getMessageRich(),
|
||||
notification.getMessageRichParameters()));
|
||||
} else {
|
||||
decryptedPushMessage.setText(notification.getMessage());
|
||||
}
|
||||
|
||||
HashMap<String, HashMap<String, String>> subjectRichParameters = notification
|
||||
.getSubjectRichParameters();
|
||||
|
||||
decryptedPushMessage.setTimestamp(notification.getDatetime().getMillis());
|
||||
|
||||
if (subjectRichParameters != null && subjectRichParameters.size() > 0) {
|
||||
HashMap<String, String> callHashMap = subjectRichParameters.get("call");
|
||||
HashMap<String, String> userHashMap = subjectRichParameters.get("user");
|
||||
HashMap<String, String> guestHashMap = subjectRichParameters.get("guest");
|
||||
|
||||
if (callHashMap != null && callHashMap.size() > 0 && callHashMap.containsKey(
|
||||
"name")) {
|
||||
if (notification.getObjectType().equals("chat")) {
|
||||
decryptedPushMessage.setSubject(callHashMap.get("name"));
|
||||
} else {
|
||||
decryptedPushMessage.setSubject(notification.getSubject());
|
||||
}
|
||||
|
||||
if (callHashMap.containsKey("call-type")) {
|
||||
conversationType = callHashMap.get("call-type");
|
||||
}
|
||||
}
|
||||
|
||||
NotificationUser notificationUser = new NotificationUser();
|
||||
if (userHashMap != null && !userHashMap.isEmpty()) {
|
||||
notificationUser.setId(userHashMap.get("id"));
|
||||
notificationUser.setType(userHashMap.get("type"));
|
||||
notificationUser.setName(userHashMap.get("name"));
|
||||
decryptedPushMessage.setNotificationUser(notificationUser);
|
||||
} else if (guestHashMap != null && !guestHashMap.isEmpty()) {
|
||||
notificationUser.setId(guestHashMap.get("id"));
|
||||
notificationUser.setType(guestHashMap.get("type"));
|
||||
notificationUser.setName(guestHashMap.get("name"));
|
||||
decryptedPushMessage.setNotificationUser(notificationUser);
|
||||
}
|
||||
}
|
||||
|
||||
showNotification(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showNotification(Intent intent) {
|
||||
int smallIcon;
|
||||
Bitmap largeIcon;
|
||||
String category;
|
||||
int priority = Notification.PRIORITY_HIGH;
|
||||
|
||||
smallIcon = R.drawable.ic_logo;
|
||||
|
||||
if (decryptedPushMessage.getType().equals("chat") || decryptedPushMessage.getType()
|
||||
.equals("room")) {
|
||||
category = Notification.CATEGORY_MESSAGE;
|
||||
} else {
|
||||
category = Notification.CATEGORY_CALL;
|
||||
}
|
||||
|
||||
switch (conversationType) {
|
||||
case "group":
|
||||
largeIcon = BitmapFactory.decodeResource(context.getResources(),
|
||||
R.drawable.ic_people_group_black_24px);
|
||||
break;
|
||||
case "public":
|
||||
largeIcon =
|
||||
BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_link_black_24px);
|
||||
break;
|
||||
default:
|
||||
// assuming one2one
|
||||
if (decryptedPushMessage.getType().equals("chat") || decryptedPushMessage.getType()
|
||||
.equals("room")) {
|
||||
largeIcon =
|
||||
BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_chat_black_24dp);
|
||||
} else {
|
||||
largeIcon =
|
||||
BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_call_black_24dp);
|
||||
}
|
||||
}
|
||||
|
||||
intent.setAction(Long.toString(System.currentTimeMillis()));
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context,
|
||||
0, intent, 0);
|
||||
|
||||
Uri uri = Uri.parse(signatureVerification.getUserEntity().getBaseUrl());
|
||||
String baseUrl = uri.getHost();
|
||||
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, "1")
|
||||
.setLargeIcon(largeIcon)
|
||||
.setSmallIcon(smallIcon)
|
||||
.setCategory(category)
|
||||
.setPriority(priority)
|
||||
.setSubText(baseUrl)
|
||||
.setWhen(decryptedPushMessage.getTimestamp())
|
||||
.setShowWhen(true)
|
||||
.setContentTitle(EmojiCompat.get().process(decryptedPushMessage.getSubject()))
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true);
|
||||
|
||||
if (!TextUtils.isEmpty(decryptedPushMessage.getText())) {
|
||||
notificationBuilder.setContentText(EmojiCompat.get().process(decryptedPushMessage.getText()));
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
// This method should exist since API 21, but some phones don't have it
|
||||
// So as a safeguard, we don't use it until 23
|
||||
notificationBuilder.setColor(context.getResources().getColor(R.color.colorPrimary));
|
||||
}
|
||||
|
||||
Bundle notificationInfo = new Bundle();
|
||||
notificationInfo.putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(),
|
||||
signatureVerification.getUserEntity().getId());
|
||||
// could be an ID or a TOKEN
|
||||
notificationInfo.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(),
|
||||
decryptedPushMessage.getId());
|
||||
notificationInfo.putLong(BundleKeys.INSTANCE.getKEY_NOTIFICATION_ID(),
|
||||
decryptedPushMessage.getNotificationId());
|
||||
notificationBuilder.setExtras(notificationInfo);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
||||
/*NotificationUtils.createNotificationChannelGroup(context,
|
||||
Long.toString(crc32.getValue()),
|
||||
groupName);*/
|
||||
|
||||
if (decryptedPushMessage.getType().equals("chat") || decryptedPushMessage.getType()
|
||||
.equals("room")) {
|
||||
NotificationUtils.INSTANCE.createNotificationChannel(context,
|
||||
NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_MESSAGES_V3(), context.getResources()
|
||||
.getString(R.string.nc_notification_channel_messages), context.getResources()
|
||||
.getString(R.string.nc_notification_channel_messages), true,
|
||||
NotificationManager.IMPORTANCE_HIGH);
|
||||
|
||||
notificationBuilder.setChannelId(
|
||||
NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_MESSAGES_V3());
|
||||
} else {
|
||||
NotificationUtils.INSTANCE.createNotificationChannel(context,
|
||||
NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_CALLS_V3(), context.getResources()
|
||||
.getString(R.string.nc_notification_channel_calls), context.getResources()
|
||||
.getString(R.string.nc_notification_channel_calls_description), true,
|
||||
NotificationManager.IMPORTANCE_HIGH);
|
||||
|
||||
notificationBuilder.setChannelId(
|
||||
NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_CALLS_V3());
|
||||
}
|
||||
} else {
|
||||
// red color for the lights
|
||||
notificationBuilder.setLights(0xFFFF0000, 200, 200);
|
||||
}
|
||||
|
||||
notificationBuilder.setContentIntent(pendingIntent);
|
||||
|
||||
CRC32 crc32 = new CRC32();
|
||||
|
||||
String groupName =
|
||||
signatureVerification.getUserEntity().getId() + "@" + decryptedPushMessage.getId();
|
||||
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.INSTANCE.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
|
||||
&& decryptedPushMessage.getNotificationUser() != null
|
||||
&& decryptedPushMessage.getType().equals("chat")) {
|
||||
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(
|
||||
EmojiCompat.get().process(decryptedPushMessage.getNotificationUser().getName()))
|
||||
.setBot(decryptedPushMessage.getNotificationUser().getType().equals("bot"));
|
||||
|
||||
notificationBuilder.setOnlyAlertOnce(true);
|
||||
|
||||
if (decryptedPushMessage.getNotificationUser().getType().equals("user")
|
||||
|| decryptedPushMessage.getNotificationUser().getType().equals("guest")) {
|
||||
String avatarUrl =
|
||||
ApiUtils.getUrlForAvatarWithName(signatureVerification.getUserEntity().getBaseUrl(),
|
||||
decryptedPushMessage.getNotificationUser().getId(), R.dimen.avatar_size);
|
||||
|
||||
if (decryptedPushMessage.getNotificationUser().getType().equals("guest")) {
|
||||
avatarUrl = ApiUtils.getUrlForAvatarWithNameForGuests(
|
||||
signatureVerification.getUserEntity().getBaseUrl(),
|
||||
decryptedPushMessage.getNotificationUser().getName(), R.dimen.avatar_size);
|
||||
}
|
||||
|
||||
ImageRequest imageRequest =
|
||||
DisplayUtils.INSTANCE.getImageRequestForUrl(avatarUrl, 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(), decryptedPushMessage.getTimestamp(),
|
||||
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);
|
||||
|
||||
if (!notification.category.equals(Notification.CATEGORY_CALL)) {
|
||||
String ringtonePreferencesString;
|
||||
Uri soundUri;
|
||||
|
||||
ringtonePreferencesString = appPreferences.getMessageRingtoneUri();
|
||||
if (TextUtils.isEmpty(ringtonePreferencesString)) {
|
||||
soundUri = Uri.parse("android.resource://" + context.getPackageName() +
|
||||
"/raw/librem_by_feandesign_message");
|
||||
} else {
|
||||
try {
|
||||
RingtoneSettings ringtoneSettings = LoganSquare.parse
|
||||
(ringtonePreferencesString, RingtoneSettings.class);
|
||||
soundUri = ringtoneSettings.getRingtoneUri();
|
||||
} catch (IOException exception) {
|
||||
soundUri = Uri.parse("android.resource://" + context.getPackageName() +
|
||||
"/raw/librem_by_feandesign_message");
|
||||
}
|
||||
}
|
||||
|
||||
if (soundUri != null && !ApplicationWideCurrentRoomHolder.getInstance().isInCall() &&
|
||||
(DoNotDisturbUtils.INSTANCE.shouldPlaySound() || importantConversation)) {
|
||||
AudioAttributes.Builder audioAttributesBuilder =
|
||||
new AudioAttributes.Builder().setContentType
|
||||
(AudioAttributes.CONTENT_TYPE_SONIFICATION);
|
||||
|
||||
if (decryptedPushMessage.getType().equals("chat") || decryptedPushMessage.getType()
|
||||
.equals("room")) {
|
||||
audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT);
|
||||
} else {
|
||||
audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST);
|
||||
}
|
||||
|
||||
MediaPlayer mediaPlayer = new MediaPlayer();
|
||||
try {
|
||||
mediaPlayer.setDataSource(context, soundUri);
|
||||
mediaPlayer.setAudioAttributes(audioAttributesBuilder.build());
|
||||
|
||||
mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start());
|
||||
mediaPlayer.setOnCompletionListener(MediaPlayer::release);
|
||||
|
||||
mediaPlayer.prepareAsync();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to set data source");
|
||||
}
|
||||
}
|
||||
|
||||
if (DoNotDisturbUtils.INSTANCE.shouldVibrate(appPreferences.getShouldVibrateSetting())
|
||||
|| importantConversation) {
|
||||
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
|
||||
if (vibrator != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
vibrator.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
} else {
|
||||
vibrator.vibrate(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Result doWork() {
|
||||
NextcloudTalkApplication.Companion.getSharedApplication()
|
||||
.getComponentApplication()
|
||||
.inject(this);
|
||||
|
||||
context = getApplicationContext();
|
||||
Data data = getInputData();
|
||||
String subject = data.getString(BundleKeys.INSTANCE.getKEY_NOTIFICATION_SUBJECT());
|
||||
String signature = data.getString(BundleKeys.INSTANCE.getKEY_NOTIFICATION_SIGNATURE());
|
||||
|
||||
try {
|
||||
byte[] base64DecodedSubject = Base64.decode(subject, Base64.DEFAULT);
|
||||
byte[] base64DecodedSignature = Base64.decode(signature, Base64.DEFAULT);
|
||||
PushUtils pushUtils = new PushUtils();
|
||||
PrivateKey privateKey = (PrivateKey) pushUtils.readKeyFromFile(false);
|
||||
|
||||
try {
|
||||
signatureVerification = pushUtils.verifySignature(base64DecodedSignature,
|
||||
base64DecodedSubject);
|
||||
|
||||
if (signatureVerification.isSignatureValid()) {
|
||||
Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
byte[] decryptedSubject = cipher.doFinal(base64DecodedSubject);
|
||||
decryptedPushMessage = LoganSquare.parse(new String(decryptedSubject),
|
||||
DecryptedPushMessage.class);
|
||||
|
||||
decryptedPushMessage.setTimestamp(System.currentTimeMillis());
|
||||
if (decryptedPushMessage.isDelete()) {
|
||||
NotificationUtils.INSTANCE.cancelExistingNotificationWithId(context,
|
||||
signatureVerification.getUserEntity(), decryptedPushMessage.getNotificationId());
|
||||
} else if (decryptedPushMessage.isDeleteAll()) {
|
||||
NotificationUtils.INSTANCE.cancelAllNotificationsForAccount(context,
|
||||
signatureVerification.getUserEntity());
|
||||
} else {
|
||||
credentials =
|
||||
ApiUtils.getCredentials(signatureVerification.getUserEntity().getUsername(),
|
||||
signatureVerification.getUserEntity().getToken());
|
||||
|
||||
ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(new
|
||||
JavaNetCookieJar(new CookieManager())).build()).build().create(NcApi.class);
|
||||
|
||||
boolean hasChatSupport = signatureVerification.getUserEntity().
|
||||
hasSpreedFeatureCapability("chat-v2");
|
||||
|
||||
boolean shouldShowNotification = decryptedPushMessage.getApp().equals("spreed");
|
||||
|
||||
if (shouldShowNotification) {
|
||||
Intent intent;
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
boolean startACall = decryptedPushMessage.getType().equals("call") || !hasChatSupport;
|
||||
if (startACall) {
|
||||
intent = new Intent(context, MagicCallActivity.class);
|
||||
} else {
|
||||
intent = new Intent(context, MainActivity.class);
|
||||
}
|
||||
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
if (!signatureVerification.getUserEntity().hasSpreedFeatureCapability
|
||||
("no-ping")) {
|
||||
bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(),
|
||||
decryptedPushMessage.getId());
|
||||
} else {
|
||||
bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(),
|
||||
decryptedPushMessage.getId());
|
||||
}
|
||||
|
||||
bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(),
|
||||
signatureVerification.getUserEntity());
|
||||
|
||||
bundle.putBoolean(BundleKeys.INSTANCE.getKEY_FROM_NOTIFICATION_START_CALL(),
|
||||
startACall);
|
||||
|
||||
intent.putExtras(bundle);
|
||||
|
||||
switch (decryptedPushMessage.getType()) {
|
||||
case "call":
|
||||
if (!bundle.containsKey(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN())) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
showNotificationForCallWithNoPing(intent);
|
||||
}
|
||||
break;
|
||||
case "room":
|
||||
if (bundle.containsKey(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN())) {
|
||||
showNotificationWithObjectData(intent);
|
||||
}
|
||||
break;
|
||||
case "chat":
|
||||
if (decryptedPushMessage.getNotificationId() != Long.MIN_VALUE) {
|
||||
showNotificationWithObjectData(intent);
|
||||
} else {
|
||||
showNotification(intent);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e1) {
|
||||
Log.d(TAG, "No proper algorithm to decrypt the message " + e1.getLocalizedMessage());
|
||||
} catch (NoSuchPaddingException e1) {
|
||||
Log.d(TAG, "No proper padding to decrypt the message " + e1.getLocalizedMessage());
|
||||
} catch (InvalidKeyException e1) {
|
||||
Log.d(TAG, "Invalid private key " + e1.getLocalizedMessage());
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
Log.d(TAG, "Something went very wrong " + exception.getLocalizedMessage());
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
}
|
762
app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt
Normal file
762
app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt
Normal file
@ -0,0 +1,762 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2018 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.jobs
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.media.AudioAttributes
|
||||
import android.media.MediaPlayer
|
||||
import android.net.Uri
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Build.VERSION_CODES
|
||||
import android.os.Bundle
|
||||
import android.os.VibrationEffect
|
||||
import android.os.Vibrator
|
||||
import android.text.TextUtils
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat.Builder
|
||||
import androidx.core.app.NotificationCompat.MessagingStyle
|
||||
import androidx.core.app.NotificationCompat.MessagingStyle.Message
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.app.Person
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.emoji.text.EmojiCompat
|
||||
import androidx.work.ListenableWorker.Result
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import autodagger.AutoInjector
|
||||
import coil.Coil
|
||||
import coil.target.Target
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.nextcloud.talk.R.color
|
||||
import com.nextcloud.talk.R.dimen
|
||||
import com.nextcloud.talk.R.drawable
|
||||
import com.nextcloud.talk.R.string
|
||||
import com.nextcloud.talk.activities.MagicCallActivity
|
||||
import com.nextcloud.talk.activities.MainActivity
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.models.RingtoneSettings
|
||||
import com.nextcloud.talk.models.SignatureVerification
|
||||
import com.nextcloud.talk.models.database.ArbitraryStorageEntity
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.models.json.chat.ChatUtils
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType.GROUP_CONVERSATION
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType.ONE_TO_ONE_CONVERSATION
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
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.newarch.utils.Images
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DoNotDisturbUtils.isDnDActive
|
||||
import com.nextcloud.talk.utils.DoNotDisturbUtils.isInDoNotDisturbWithPriority
|
||||
import com.nextcloud.talk.utils.DoNotDisturbUtils.shouldPlaySound
|
||||
import com.nextcloud.talk.utils.DoNotDisturbUtils.shouldVibrate
|
||||
import com.nextcloud.talk.utils.NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V3
|
||||
import com.nextcloud.talk.utils.NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES_V3
|
||||
import com.nextcloud.talk.utils.NotificationUtils.cancelAllNotificationsForAccount
|
||||
import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationWithId
|
||||
import com.nextcloud.talk.utils.NotificationUtils.createNotificationChannel
|
||||
import com.nextcloud.talk.utils.NotificationUtils.findNotificationForRoom
|
||||
import com.nextcloud.talk.utils.PushUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CALL
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_SIGNATURE
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_SUBJECT
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||
import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.disposables.Disposable
|
||||
import okhttp3.JavaNetCookieJar
|
||||
import okhttp3.OkHttpClient
|
||||
import org.parceler.Parcels
|
||||
import retrofit2.Retrofit
|
||||
import java.io.IOException
|
||||
import java.net.CookieManager
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.PrivateKey
|
||||
import java.util.HashMap
|
||||
import java.util.function.Consumer
|
||||
import java.util.zip.CRC32
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.NoSuchPaddingException
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class NotificationWorker(
|
||||
context: Context,
|
||||
workerParams: WorkerParameters
|
||||
) : Worker(context, workerParams) {
|
||||
@Inject
|
||||
internal var appPreferences: AppPreferences? = null
|
||||
@Inject
|
||||
internal var arbitraryStorageUtils: ArbitraryStorageUtils? =
|
||||
null
|
||||
@Inject
|
||||
internal var retrofit: Retrofit? = null
|
||||
@Inject
|
||||
internal var okHttpClient: OkHttpClient? = null
|
||||
private var ncApi: NcApi? = null
|
||||
private var decryptedPushMessage: DecryptedPushMessage? = null
|
||||
private var context: Context? = null
|
||||
private var signatureVerification: SignatureVerification? = null
|
||||
private var conversationType: String? = "one2one"
|
||||
private var credentials: String? = null
|
||||
private var muteCall = false
|
||||
private var importantConversation = false
|
||||
private fun showNotificationForCallWithNoPing(intent: Intent) {
|
||||
val userEntity: UserEntity =
|
||||
signatureVerification!!.userEntity
|
||||
var arbitraryStorageEntity: ArbitraryStorageEntity
|
||||
if (arbitraryStorageUtils!!.getStorageSetting(
|
||||
userEntity.id,
|
||||
"mute_calls",
|
||||
intent.extras!!.getString(KEY_ROOM_TOKEN)
|
||||
).also { arbitraryStorageEntity = it }
|
||||
!= null
|
||||
) {
|
||||
muteCall = arbitraryStorageEntity.value!!.toBoolean()
|
||||
}
|
||||
if (arbitraryStorageUtils!!.getStorageSetting(
|
||||
userEntity.id,
|
||||
"important_conversation",
|
||||
intent.extras!!.getString(KEY_ROOM_TOKEN)
|
||||
).also { arbitraryStorageEntity = it } != null
|
||||
) {
|
||||
importantConversation = arbitraryStorageEntity.value!!.toBoolean()
|
||||
}
|
||||
if (isDnDActive()) {
|
||||
if (!isInDoNotDisturbWithPriority()
|
||||
|| !importantConversation
|
||||
|| muteCall
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
ncApi!!.getRoom(
|
||||
credentials, ApiUtils.getRoom(
|
||||
userEntity.baseUrl,
|
||||
intent.extras!!.getString(KEY_ROOM_TOKEN)
|
||||
)
|
||||
)
|
||||
.blockingSubscribe(object : Observer<RoomOverall?> {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
val conversation: Conversation =
|
||||
roomOverall.getOcs()
|
||||
.getData()
|
||||
intent.putExtra(
|
||||
KEY_ROOM,
|
||||
Parcels.wrap(
|
||||
conversation
|
||||
)
|
||||
)
|
||||
if ((conversation.type
|
||||
== ONE_TO_ONE_CONVERSATION) ||
|
||||
!TextUtils.isEmpty(
|
||||
conversation.objectType
|
||||
) && "share:password" == conversation.objectType
|
||||
) {
|
||||
context!!.startActivity(intent)
|
||||
} else {
|
||||
conversationType = if (conversation.type == GROUP_CONVERSATION) {
|
||||
"group"
|
||||
} else {
|
||||
"public"
|
||||
}
|
||||
if (decryptedPushMessage!!.notificationId != Long.MIN_VALUE) {
|
||||
showNotificationWithObjectData(intent)
|
||||
} else {
|
||||
showNotification(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {}
|
||||
override fun onComplete() {}
|
||||
})
|
||||
}
|
||||
|
||||
private fun showNotificationWithObjectData(intent: Intent) {
|
||||
val userEntity: UserEntity =
|
||||
signatureVerification!!.userEntity
|
||||
ncApi!!.getNotification(
|
||||
credentials, ApiUtils.getUrlForNotificationWithId(
|
||||
userEntity.baseUrl,
|
||||
decryptedPushMessage!!.notificationId.toString()
|
||||
)
|
||||
)
|
||||
.blockingSubscribe(object : Observer<NotificationOverall?> {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onNext(notificationOverall: NotificationOverall) {
|
||||
val notification: com.nextcloud.talk.models.json.notifications.Notification =
|
||||
notificationOverall.ocs
|
||||
.notification
|
||||
if (notification.messageRichParameters != null &&
|
||||
notification.messageRichParameters.size > 0
|
||||
) {
|
||||
decryptedPushMessage!!.text = ChatUtils.getParsedMessage(
|
||||
notification.messageRich,
|
||||
notification.messageRichParameters
|
||||
)
|
||||
} else {
|
||||
decryptedPushMessage!!.text = notification.message
|
||||
}
|
||||
val subjectRichParameters: HashMap<String, HashMap<String, String>>? =
|
||||
notification
|
||||
.subjectRichParameters
|
||||
decryptedPushMessage!!.timestamp = notification.datetime.millis
|
||||
if (subjectRichParameters != null && subjectRichParameters.size > 0) {
|
||||
val callHashMap =
|
||||
subjectRichParameters["call"]
|
||||
val userHashMap =
|
||||
subjectRichParameters["user"]
|
||||
val guestHashMap =
|
||||
subjectRichParameters["guest"]
|
||||
if (callHashMap != null && callHashMap.size > 0 && callHashMap.containsKey(
|
||||
"name"
|
||||
)
|
||||
) {
|
||||
if (notification.objectType == "chat") {
|
||||
decryptedPushMessage!!.subject = callHashMap["name"]
|
||||
} else {
|
||||
decryptedPushMessage!!.subject = notification.subject
|
||||
}
|
||||
if (callHashMap.containsKey("call-type")) {
|
||||
conversationType = callHashMap["call-type"]
|
||||
}
|
||||
}
|
||||
val notificationUser =
|
||||
NotificationUser()
|
||||
if (userHashMap != null && !userHashMap.isEmpty()) {
|
||||
notificationUser.id = userHashMap["id"]
|
||||
notificationUser.type = userHashMap["type"]
|
||||
notificationUser.name = userHashMap["name"]
|
||||
decryptedPushMessage!!.notificationUser = notificationUser
|
||||
} else if (guestHashMap != null && !guestHashMap.isEmpty()) {
|
||||
notificationUser.id = guestHashMap["id"]
|
||||
notificationUser.type = guestHashMap["type"]
|
||||
notificationUser.name = guestHashMap["name"]
|
||||
decryptedPushMessage!!.notificationUser = notificationUser
|
||||
}
|
||||
}
|
||||
showNotification(intent)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {}
|
||||
override fun onComplete() {}
|
||||
})
|
||||
}
|
||||
|
||||
private fun showNotification(intent: Intent) {
|
||||
val smallIcon: Int
|
||||
val largeIcon: Bitmap?
|
||||
val category: String
|
||||
val priority = Notification.PRIORITY_HIGH
|
||||
smallIcon = drawable.ic_logo
|
||||
category = if (decryptedPushMessage!!.type == "chat" || (decryptedPushMessage!!.type
|
||||
== "room")
|
||||
) {
|
||||
Notification.CATEGORY_MESSAGE
|
||||
} else {
|
||||
Notification.CATEGORY_CALL
|
||||
}
|
||||
largeIcon = when (conversationType) {
|
||||
"group" -> BitmapFactory.decodeResource(
|
||||
context!!.resources,
|
||||
drawable.ic_people_group_black_24px
|
||||
)
|
||||
"public" -> BitmapFactory.decodeResource(
|
||||
context!!.resources, drawable.ic_link_black_24px
|
||||
)
|
||||
else -> // assuming one2one
|
||||
if (decryptedPushMessage!!.type == "chat" || (decryptedPushMessage!!.type
|
||||
== "room")
|
||||
) {
|
||||
BitmapFactory.decodeResource(
|
||||
context!!.resources, drawable.ic_chat_black_24dp
|
||||
)
|
||||
} else {
|
||||
BitmapFactory.decodeResource(
|
||||
context!!.resources, drawable.ic_call_black_24dp
|
||||
)
|
||||
}
|
||||
}
|
||||
intent.action = System.currentTimeMillis()
|
||||
.toString()
|
||||
val pendingIntent: PendingIntent? = PendingIntent.getActivity(
|
||||
context,
|
||||
0, intent, 0
|
||||
)
|
||||
val uri: Uri =
|
||||
Uri.parse(signatureVerification!!.userEntity.baseUrl)
|
||||
val baseUrl = uri.host
|
||||
val notificationBuilder: Builder =
|
||||
Builder(context!!, "1")
|
||||
.setLargeIcon(largeIcon)
|
||||
.setSmallIcon(smallIcon)
|
||||
.setCategory(category)
|
||||
.setPriority(priority)
|
||||
.setSubText(baseUrl)
|
||||
.setWhen(decryptedPushMessage!!.timestamp)
|
||||
.setShowWhen(true)
|
||||
.setContentTitle(
|
||||
EmojiCompat.get().process(decryptedPushMessage!!.subject)
|
||||
)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
if (!TextUtils.isEmpty(decryptedPushMessage!!.text)) {
|
||||
notificationBuilder.setContentText(
|
||||
EmojiCompat.get().process(decryptedPushMessage!!.text)
|
||||
)
|
||||
}
|
||||
if (VERSION.SDK_INT >= 23) {
|
||||
// This method should exist since API 21, but some phones don't have it
|
||||
// So as a safeguard, we don't use it until 23
|
||||
|
||||
notificationBuilder.color = context!!.resources.getColor(color.colorPrimary)
|
||||
}
|
||||
val notificationInfo = Bundle()
|
||||
notificationInfo.putLong(
|
||||
KEY_INTERNAL_USER_ID,
|
||||
signatureVerification!!.userEntity.id
|
||||
)
|
||||
// could be an ID or a TOKEN
|
||||
|
||||
notificationInfo.putString(
|
||||
KEY_ROOM_TOKEN,
|
||||
decryptedPushMessage!!.id
|
||||
)
|
||||
notificationInfo.putLong(
|
||||
KEY_NOTIFICATION_ID,
|
||||
decryptedPushMessage!!.notificationId
|
||||
)
|
||||
notificationBuilder.extras = notificationInfo
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.O) {
|
||||
|
||||
/*NotificationUtils.createNotificationChannelGroup(context,
|
||||
Long.toString(crc32.getValue()),
|
||||
groupName);*/
|
||||
|
||||
if (decryptedPushMessage!!.type == "chat" || (decryptedPushMessage!!.type
|
||||
== "room")
|
||||
) {
|
||||
createNotificationChannel(
|
||||
context!!,
|
||||
NOTIFICATION_CHANNEL_MESSAGES_V3,
|
||||
context!!.resources
|
||||
.getString(string.nc_notification_channel_messages),
|
||||
context!!.resources
|
||||
.getString(string.nc_notification_channel_messages), true,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
)
|
||||
notificationBuilder.setChannelId(
|
||||
NOTIFICATION_CHANNEL_MESSAGES_V3
|
||||
)
|
||||
} else {
|
||||
createNotificationChannel(
|
||||
context!!,
|
||||
NOTIFICATION_CHANNEL_CALLS_V3,
|
||||
context!!.resources
|
||||
.getString(string.nc_notification_channel_calls),
|
||||
context!!.resources
|
||||
.getString(string.nc_notification_channel_calls_description),
|
||||
true,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
)
|
||||
notificationBuilder.setChannelId(
|
||||
NOTIFICATION_CHANNEL_CALLS_V3
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// red color for the lights
|
||||
|
||||
notificationBuilder.setLights(-0x10000, 200, 200)
|
||||
}
|
||||
notificationBuilder.setContentIntent(pendingIntent)
|
||||
var crc32 = CRC32()
|
||||
val groupName =
|
||||
signatureVerification!!.userEntity.id.toString() + "@" + decryptedPushMessage!!.id
|
||||
crc32.update(groupName.toByteArray())
|
||||
notificationBuilder.setGroup(crc32.value.toString())
|
||||
|
||||
// notificationId
|
||||
|
||||
crc32 = CRC32()
|
||||
val stringForCrc = System.currentTimeMillis()
|
||||
.toString()
|
||||
crc32.update(stringForCrc.toByteArray())
|
||||
val activeStatusBarNotification =
|
||||
findNotificationForRoom(
|
||||
context,
|
||||
signatureVerification!!.userEntity, decryptedPushMessage!!.id
|
||||
)
|
||||
val notificationId: Int
|
||||
notificationId = activeStatusBarNotification?.id ?: crc32.value
|
||||
.toInt()
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.N && decryptedPushMessage!!.notificationUser != null && decryptedPushMessage!!.type == "chat") {
|
||||
var style: MessagingStyle? = null
|
||||
if (activeStatusBarNotification != null) {
|
||||
val activeNotification: Notification? =
|
||||
activeStatusBarNotification.notification
|
||||
style =
|
||||
MessagingStyle.extractMessagingStyleFromNotification(
|
||||
activeNotification
|
||||
)
|
||||
}
|
||||
val person = Person.Builder()
|
||||
.setKey(
|
||||
signatureVerification!!.userEntity.id.toString() +
|
||||
"@" + decryptedPushMessage!!.notificationUser.id
|
||||
)
|
||||
.setName(
|
||||
EmojiCompat.get().process(
|
||||
decryptedPushMessage!!.notificationUser.name
|
||||
)
|
||||
)
|
||||
.setBot(decryptedPushMessage!!.notificationUser.type == "bot")
|
||||
notificationBuilder.setOnlyAlertOnce(true)
|
||||
if (decryptedPushMessage!!.notificationUser.type == "user" || decryptedPushMessage!!.notificationUser.type == "guest") {
|
||||
var avatarUrl: String? = ApiUtils.getUrlForAvatarWithName(
|
||||
signatureVerification!!.userEntity.baseUrl,
|
||||
decryptedPushMessage!!.notificationUser.id,
|
||||
dimen.avatar_size
|
||||
)
|
||||
if (decryptedPushMessage!!.notificationUser.type == "guest") {
|
||||
avatarUrl = ApiUtils.getUrlForAvatarWithNameForGuests(
|
||||
signatureVerification!!.userEntity.baseUrl,
|
||||
decryptedPushMessage!!.notificationUser.name,
|
||||
dimen.avatar_size
|
||||
)
|
||||
}
|
||||
|
||||
val target = object : Target {
|
||||
override fun onSuccess(result: Drawable) {
|
||||
super.onSuccess(result)
|
||||
person.setIcon(IconCompat.createWithBitmap(result.toBitmap()))
|
||||
notificationBuilder.setStyle(
|
||||
getStyle(
|
||||
person.build(),
|
||||
style
|
||||
)
|
||||
)
|
||||
sendNotificationWithId(notificationId, notificationBuilder.build())
|
||||
}
|
||||
|
||||
override fun onError(error: Drawable?) {
|
||||
super.onError(error)
|
||||
notificationBuilder.setStyle(getStyle(person.build(), style))
|
||||
sendNotificationWithId(notificationId, notificationBuilder.build())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val request = Images().getRequestForUrl(
|
||||
Coil.loader(), context!!, avatarUrl!!, null,
|
||||
target, null, CircleCropTransformation()
|
||||
)
|
||||
|
||||
Coil.loader()
|
||||
.load(request)
|
||||
} else {
|
||||
notificationBuilder.setStyle(getStyle(person.build(), style))
|
||||
sendNotificationWithId(notificationId, notificationBuilder.build())
|
||||
}
|
||||
} else {
|
||||
sendNotificationWithId(notificationId, notificationBuilder.build())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStyle(
|
||||
person: Person,
|
||||
style: MessagingStyle?
|
||||
): MessagingStyle? {
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.N) {
|
||||
val newStyle =
|
||||
MessagingStyle(person)
|
||||
newStyle.conversationTitle = decryptedPushMessage!!.subject
|
||||
newStyle.isGroupConversation = conversationType != "one2one"
|
||||
style?.messages?.forEach(
|
||||
Consumer { message: Message ->
|
||||
newStyle.addMessage(
|
||||
Message(
|
||||
message.text,
|
||||
message.timestamp, message.person
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
newStyle.addMessage(
|
||||
decryptedPushMessage!!.text, decryptedPushMessage!!.timestamp,
|
||||
person
|
||||
)
|
||||
return newStyle
|
||||
}
|
||||
|
||||
// we'll never come here
|
||||
|
||||
return style
|
||||
}
|
||||
|
||||
private fun sendNotificationWithId(
|
||||
notificationId: Int,
|
||||
notification: Notification
|
||||
) {
|
||||
val notificationManager =
|
||||
NotificationManagerCompat.from(context!!)
|
||||
notificationManager.notify(notificationId, notification)
|
||||
if (notification.category != Notification.CATEGORY_CALL) {
|
||||
val ringtonePreferencesString: String?
|
||||
val soundUri: Uri?
|
||||
ringtonePreferencesString = appPreferences!!.messageRingtoneUri
|
||||
soundUri = if (TextUtils.isEmpty(ringtonePreferencesString)) {
|
||||
Uri.parse(
|
||||
"android.resource://" + context!!.packageName +
|
||||
"/raw/librem_by_feandesign_message"
|
||||
)
|
||||
} else {
|
||||
try {
|
||||
val ringtoneSettings: RingtoneSettings =
|
||||
LoganSquare.parse<RingtoneSettings>(
|
||||
ringtonePreferencesString, RingtoneSettings::class.java
|
||||
)
|
||||
ringtoneSettings.getRingtoneUri()
|
||||
} catch (exception: IOException) {
|
||||
Uri.parse(
|
||||
"android.resource://" + context!!.packageName +
|
||||
"/raw/librem_by_feandesign_message"
|
||||
)
|
||||
}
|
||||
}
|
||||
if (soundUri != null && !ApplicationWideCurrentRoomHolder.getInstance().isInCall &&
|
||||
(shouldPlaySound() || importantConversation)
|
||||
) {
|
||||
val audioAttributesBuilder: AudioAttributes.Builder =
|
||||
AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
if (decryptedPushMessage!!.type == "chat" || (decryptedPushMessage!!.type
|
||||
== "room")
|
||||
) {
|
||||
audioAttributesBuilder.setUsage(
|
||||
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT
|
||||
)
|
||||
} else {
|
||||
audioAttributesBuilder.setUsage(
|
||||
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST
|
||||
)
|
||||
}
|
||||
val mediaPlayer = MediaPlayer()
|
||||
try {
|
||||
mediaPlayer.setDataSource(context!!, soundUri)
|
||||
mediaPlayer.setAudioAttributes(audioAttributesBuilder.build())
|
||||
mediaPlayer.setOnPreparedListener { mp: MediaPlayer? -> mediaPlayer.start() }
|
||||
mediaPlayer.setOnCompletionListener { obj: MediaPlayer -> obj.release() }
|
||||
mediaPlayer.prepareAsync()
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Failed to set data source")
|
||||
}
|
||||
}
|
||||
if (shouldVibrate(
|
||||
appPreferences!!.shouldVibrateSetting
|
||||
)
|
||||
|| importantConversation
|
||||
) {
|
||||
val vibrator =
|
||||
context!!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.O) {
|
||||
vibrator.vibrate(
|
||||
VibrationEffect.createOneShot(
|
||||
500, VibrationEffect.DEFAULT_AMPLITUDE
|
||||
)
|
||||
)
|
||||
} else {
|
||||
vibrator.vibrate(500)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun doWork(): Result {
|
||||
sharedApplication!!.componentApplication.inject(this)
|
||||
context = applicationContext
|
||||
val data = inputData
|
||||
val subject =
|
||||
data.getString(KEY_NOTIFICATION_SUBJECT)
|
||||
val signature =
|
||||
data.getString(KEY_NOTIFICATION_SIGNATURE)
|
||||
try {
|
||||
val base64DecodedSubject: ByteArray? =
|
||||
Base64.decode(subject, Base64.DEFAULT)
|
||||
val base64DecodedSignature: ByteArray? =
|
||||
Base64.decode(signature, Base64.DEFAULT)
|
||||
val pushUtils = PushUtils()
|
||||
val privateKey =
|
||||
pushUtils.readKeyFromFile(false) as PrivateKey
|
||||
try {
|
||||
signatureVerification = pushUtils.verifySignature(
|
||||
base64DecodedSignature,
|
||||
base64DecodedSubject
|
||||
)
|
||||
if (signatureVerification!!.isSignatureValid()) {
|
||||
val cipher: Cipher = Cipher.getInstance("RSA/None/PKCS1Padding")
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey)
|
||||
val decryptedSubject: ByteArray? = cipher.doFinal(base64DecodedSubject)
|
||||
decryptedPushMessage =
|
||||
LoganSquare.parse<DecryptedPushMessage>(
|
||||
String(decryptedSubject!!),
|
||||
DecryptedPushMessage::class.java
|
||||
)
|
||||
decryptedPushMessage!!.timestamp = System.currentTimeMillis()
|
||||
if (decryptedPushMessage!!.isDelete) {
|
||||
cancelExistingNotificationWithId(
|
||||
context,
|
||||
signatureVerification!!.getUserEntity(), decryptedPushMessage!!.notificationId
|
||||
)
|
||||
} else if (decryptedPushMessage!!.isDeleteAll) {
|
||||
cancelAllNotificationsForAccount(
|
||||
context,
|
||||
signatureVerification!!.getUserEntity()
|
||||
)
|
||||
} else {
|
||||
credentials = ApiUtils.getCredentials(
|
||||
signatureVerification!!.getUserEntity().username,
|
||||
signatureVerification!!.getUserEntity().token
|
||||
)
|
||||
ncApi = retrofit!!.newBuilder()
|
||||
.client(
|
||||
okHttpClient!!.newBuilder().cookieJar(
|
||||
JavaNetCookieJar(CookieManager())
|
||||
).build()
|
||||
)
|
||||
.build()
|
||||
.create(
|
||||
NcApi::class.java
|
||||
)
|
||||
val hasChatSupport = signatureVerification!!.getUserEntity()
|
||||
.hasSpreedFeatureCapability("chat-v2")
|
||||
val shouldShowNotification = decryptedPushMessage!!.app == "spreed"
|
||||
if (shouldShowNotification) {
|
||||
val intent: Intent
|
||||
val bundle = Bundle()
|
||||
val startACall =
|
||||
decryptedPushMessage!!.type == "call" || !hasChatSupport
|
||||
intent = if (startACall) {
|
||||
Intent(
|
||||
context, MagicCallActivity::class.java
|
||||
)
|
||||
} else {
|
||||
Intent(
|
||||
context, MainActivity::class.java
|
||||
)
|
||||
}
|
||||
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
if (!signatureVerification!!.getUserEntity().hasSpreedFeatureCapability("no-ping")) {
|
||||
bundle.putString(
|
||||
KEY_ROOM_ID,
|
||||
decryptedPushMessage!!.id
|
||||
)
|
||||
} else {
|
||||
bundle.putString(
|
||||
KEY_ROOM_TOKEN,
|
||||
decryptedPushMessage!!.id
|
||||
)
|
||||
}
|
||||
bundle.putParcelable(
|
||||
KEY_USER_ENTITY,
|
||||
signatureVerification!!.getUserEntity()
|
||||
)
|
||||
bundle.putBoolean(
|
||||
KEY_FROM_NOTIFICATION_START_CALL,
|
||||
startACall
|
||||
)
|
||||
intent.putExtras(bundle)
|
||||
when (decryptedPushMessage!!.type) {
|
||||
"call" -> if (!bundle.containsKey(
|
||||
KEY_ROOM_TOKEN
|
||||
)
|
||||
) {
|
||||
context!!.startActivity(intent)
|
||||
} else {
|
||||
showNotificationForCallWithNoPing(intent)
|
||||
}
|
||||
"room" -> if (bundle.containsKey(
|
||||
KEY_ROOM_TOKEN
|
||||
)
|
||||
) {
|
||||
showNotificationWithObjectData(intent)
|
||||
}
|
||||
"chat" -> if (decryptedPushMessage!!.notificationId != Long.MIN_VALUE) {
|
||||
showNotificationWithObjectData(intent)
|
||||
} else {
|
||||
showNotification(intent)
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e1: NoSuchAlgorithmException) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"No proper algorithm to decrypt the message " + e1.localizedMessage
|
||||
)
|
||||
} catch (e1: NoSuchPaddingException) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"No proper padding to decrypt the message " + e1.localizedMessage
|
||||
)
|
||||
} catch (e1: InvalidKeyException) {
|
||||
Log.d(
|
||||
TAG, "Invalid private key " + e1.localizedMessage
|
||||
)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.d(
|
||||
TAG, "Something went very wrong " + exception.localizedMessage
|
||||
)
|
||||
}
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "NotificationWorker"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user