use of eu.siacs.conversations.entities.Conversation in project Conversations by siacs.
the class NotificationService method pushFailedDelivery.
public void pushFailedDelivery(final Message message) {
final Conversation conversation = (Conversation) message.getConversation();
final boolean isScreenLocked = !mXmppConnectionService.isScreenLocked();
if (this.mIsInForeground && isScreenLocked && this.mOpenConversation == message.getConversation()) {
Log.d(Config.LOGTAG, message.getConversation().getAccount().getJid().asBareJid() + ": suppressing failed delivery notification because conversation is open");
return;
}
final PendingIntent pendingIntent = createContentIntent(conversation);
final int notificationId = generateRequestCode(conversation, 0) + DELIVERY_FAILED_NOTIFICATION_ID;
final int failedDeliveries = conversation.countFailedDeliveries();
final Notification notification = new Builder(mXmppConnectionService, "delivery_failed").setContentTitle(conversation.getName()).setAutoCancel(true).setSmallIcon(R.drawable.ic_error_white_24dp).setContentText(mXmppConnectionService.getResources().getQuantityText(R.plurals.some_messages_could_not_be_delivered, failedDeliveries)).setGroup("delivery_failed").setContentIntent(pendingIntent).build();
final Notification summaryNotification = new Builder(mXmppConnectionService, "delivery_failed").setContentTitle(mXmppConnectionService.getString(R.string.failed_deliveries)).setContentText(mXmppConnectionService.getResources().getQuantityText(R.plurals.some_messages_could_not_be_delivered, 1024)).setSmallIcon(R.drawable.ic_error_white_24dp).setGroup("delivery_failed").setGroupSummary(true).setAutoCancel(true).build();
notify(notificationId, notification);
notify(DELIVERY_FAILED_NOTIFICATION_ID, summaryNotification);
}
use of eu.siacs.conversations.entities.Conversation in project Conversations by siacs.
the class NotificationService method buildSingleConversations.
private Builder buildSingleConversations(final ArrayList<Message> messages, final boolean notify, final boolean quietHours) {
final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, quietHours ? "quiet_hours" : (notify ? "messages" : "silent_messages"));
if (messages.size() >= 1) {
final Conversation conversation = (Conversation) messages.get(0).getConversation();
mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService().get(conversation, AvatarService.getSystemUiAvatarSize(mXmppConnectionService)));
mBuilder.setContentTitle(conversation.getName());
if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) {
int count = messages.size();
mBuilder.setContentText(mXmppConnectionService.getResources().getQuantityString(R.plurals.x_messages, count, count));
} else {
Message message;
// TODO starting with Android 9 we might want to put images in MessageStyle
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P && (message = getImage(messages)) != null) {
modifyForImage(mBuilder, message, messages);
} else {
modifyForTextOnly(mBuilder, messages);
}
RemoteInput remoteInput = new RemoteInput.Builder("text_reply").setLabel(UIHelper.getMessageHint(mXmppConnectionService, conversation)).build();
PendingIntent markAsReadPendingIntent = createReadPendingIntent(conversation);
NotificationCompat.Action markReadAction = new NotificationCompat.Action.Builder(R.drawable.ic_drafts_white_24dp, mXmppConnectionService.getString(R.string.mark_as_read), markAsReadPendingIntent).setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ).setShowsUserInterface(false).build();
final String replyLabel = mXmppConnectionService.getString(R.string.reply);
final String lastMessageUuid = Iterables.getLast(messages).getUuid();
final NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(R.drawable.ic_send_text_offline, replyLabel, createReplyIntent(conversation, lastMessageUuid, false)).setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY).setShowsUserInterface(false).addRemoteInput(remoteInput).build();
final NotificationCompat.Action wearReplyAction = new NotificationCompat.Action.Builder(R.drawable.ic_wear_reply, replyLabel, createReplyIntent(conversation, lastMessageUuid, true)).addRemoteInput(remoteInput).build();
mBuilder.extend(new NotificationCompat.WearableExtender().addAction(wearReplyAction));
int addedActionsCount = 1;
mBuilder.addAction(markReadAction);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mBuilder.addAction(replyAction);
++addedActionsCount;
}
if (displaySnoozeAction(messages)) {
String label = mXmppConnectionService.getString(R.string.snooze);
PendingIntent pendingSnoozeIntent = createSnoozeIntent(conversation);
NotificationCompat.Action snoozeAction = new NotificationCompat.Action.Builder(R.drawable.ic_notifications_paused_white_24dp, label, pendingSnoozeIntent).build();
mBuilder.addAction(snoozeAction);
++addedActionsCount;
}
if (addedActionsCount < 3) {
final Message firstLocationMessage = getFirstLocationMessage(messages);
if (firstLocationMessage != null) {
final PendingIntent pendingShowLocationIntent = createShowLocationIntent(firstLocationMessage);
if (pendingShowLocationIntent != null) {
final String label = mXmppConnectionService.getResources().getString(R.string.show_location);
NotificationCompat.Action locationAction = new NotificationCompat.Action.Builder(R.drawable.ic_room_white_24dp, label, pendingShowLocationIntent).build();
mBuilder.addAction(locationAction);
++addedActionsCount;
}
}
}
if (addedActionsCount < 3) {
Message firstDownloadableMessage = getFirstDownloadableMessage(messages);
if (firstDownloadableMessage != null) {
String label = mXmppConnectionService.getResources().getString(R.string.download_x_file, UIHelper.getFileDescriptionString(mXmppConnectionService, firstDownloadableMessage));
PendingIntent pendingDownloadIntent = createDownloadIntent(firstDownloadableMessage);
NotificationCompat.Action downloadAction = new NotificationCompat.Action.Builder(R.drawable.ic_file_download_white_24dp, label, pendingDownloadIntent).build();
mBuilder.addAction(downloadAction);
++addedActionsCount;
}
}
}
if (conversation.getMode() == Conversation.MODE_SINGLE) {
Contact contact = conversation.getContact();
Uri systemAccount = contact.getSystemAccount();
if (systemAccount != null) {
mBuilder.addPerson(systemAccount.toString());
}
}
mBuilder.setWhen(conversation.getLatestMessage().getTimeSent());
mBuilder.setSmallIcon(R.drawable.ic_notification);
mBuilder.setDeleteIntent(createDeleteIntent(conversation));
mBuilder.setContentIntent(createContentIntent(conversation));
}
return mBuilder;
}
use of eu.siacs.conversations.entities.Conversation in project Conversations by siacs.
the class XmppConnectionService method sendMessage.
private void sendMessage(final Message message, final boolean resend, final boolean delay) {
final Account account = message.getConversation().getAccount();
if (account.setShowErrorNotification(true)) {
databaseBackend.updateAccount(account);
mNotificationService.updateErrorNotification();
}
final Conversation conversation = (Conversation) message.getConversation();
account.deactivateGracePeriod();
if (QuickConversationsService.isQuicksy() && conversation.getMode() == Conversation.MODE_SINGLE) {
final Contact contact = conversation.getContact();
if (!contact.showInRoster() && contact.getOption(Contact.Options.SYNCED_VIA_OTHER)) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": adding " + contact.getJid() + " on sending message");
createContact(contact, true);
}
}
MessagePacket packet = null;
final boolean addToConversation = (conversation.getMode() != Conversation.MODE_MULTI || !Patches.BAD_MUC_REFLECTION.contains(account.getServerIdentity())) && !message.edited();
boolean saveInDb = addToConversation;
message.setStatus(Message.STATUS_WAITING);
if (message.getEncryption() != Message.ENCRYPTION_NONE && conversation.getMode() == Conversation.MODE_MULTI && conversation.isPrivateAndNonAnonymous()) {
if (conversation.setAttribute(Conversation.ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS, true)) {
databaseBackend.updateConversation(conversation);
}
}
final boolean inProgressJoin = isJoinInProgress(conversation);
if (account.isOnlineAndConnected() && !inProgressJoin) {
switch(message.getEncryption()) {
case Message.ENCRYPTION_NONE:
if (message.needsUploading()) {
if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize()) || conversation.getMode() == Conversation.MODE_MULTI || message.fixCounterpart()) {
this.sendFileMessage(message, delay);
} else {
break;
}
} else {
packet = mMessageGenerator.generateChat(message);
}
break;
case Message.ENCRYPTION_PGP:
case Message.ENCRYPTION_DECRYPTED:
if (message.needsUploading()) {
if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize()) || conversation.getMode() == Conversation.MODE_MULTI || message.fixCounterpart()) {
this.sendFileMessage(message, delay);
} else {
break;
}
} else {
packet = mMessageGenerator.generatePgpChat(message);
}
break;
case Message.ENCRYPTION_AXOLOTL:
message.setFingerprint(account.getAxolotlService().getOwnFingerprint());
if (message.needsUploading()) {
if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize()) || conversation.getMode() == Conversation.MODE_MULTI || message.fixCounterpart()) {
this.sendFileMessage(message, delay);
} else {
break;
}
} else {
XmppAxolotlMessage axolotlMessage = account.getAxolotlService().fetchAxolotlMessageFromCache(message);
if (axolotlMessage == null) {
account.getAxolotlService().preparePayloadMessage(message, delay);
} else {
packet = mMessageGenerator.generateAxolotlChat(message, axolotlMessage);
}
}
break;
}
if (packet != null) {
if (account.getXmppConnection().getFeatures().sm() || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) {
message.setStatus(Message.STATUS_UNSEND);
} else {
message.setStatus(Message.STATUS_SEND);
}
}
} else {
switch(message.getEncryption()) {
case Message.ENCRYPTION_DECRYPTED:
if (!message.needsUploading()) {
String pgpBody = message.getEncryptedBody();
String decryptedBody = message.getBody();
// TODO might throw NPE
message.setBody(pgpBody);
message.setEncryption(Message.ENCRYPTION_PGP);
if (message.edited()) {
message.setBody(decryptedBody);
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
if (!databaseBackend.updateMessage(message, message.getEditedId())) {
Log.e(Config.LOGTAG, "error updated message in DB after edit");
}
updateConversationUi();
return;
} else {
databaseBackend.createMessage(message);
saveInDb = false;
message.setBody(decryptedBody);
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
}
}
break;
case Message.ENCRYPTION_AXOLOTL:
message.setFingerprint(account.getAxolotlService().getOwnFingerprint());
break;
}
}
boolean mucMessage = conversation.getMode() == Conversation.MODE_MULTI && !message.isPrivateMessage();
if (mucMessage) {
message.setCounterpart(conversation.getMucOptions().getSelf().getFullJid());
}
if (resend) {
if (packet != null && addToConversation) {
if (account.getXmppConnection().getFeatures().sm() || mucMessage) {
markMessage(message, Message.STATUS_UNSEND);
} else {
markMessage(message, Message.STATUS_SEND);
}
}
} else {
if (addToConversation) {
conversation.add(message);
}
if (saveInDb) {
databaseBackend.createMessage(message);
} else if (message.edited()) {
if (!databaseBackend.updateMessage(message, message.getEditedId())) {
Log.e(Config.LOGTAG, "error updated message in DB after edit");
}
}
updateConversationUi();
}
if (packet != null) {
if (delay) {
mMessageGenerator.addDelay(packet, message.getTimeSent());
}
if (conversation.setOutgoingChatState(Config.DEFAULT_CHAT_STATE)) {
if (this.sendChatStates()) {
packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
}
}
sendMessagePacket(account, packet);
}
}
use of eu.siacs.conversations.entities.Conversation in project Conversations by siacs.
the class XmppConnectionService method restoreFromDatabase.
private void restoreFromDatabase() {
synchronized (this.conversations) {
final Map<String, Account> accountLookupTable = new Hashtable<>();
for (Account account : this.accounts) {
accountLookupTable.put(account.getUuid(), account);
}
Log.d(Config.LOGTAG, "restoring conversations...");
final long startTimeConversationsRestore = SystemClock.elapsedRealtime();
this.conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_AVAILABLE));
for (Iterator<Conversation> iterator = conversations.listIterator(); iterator.hasNext(); ) {
Conversation conversation = iterator.next();
Account account = accountLookupTable.get(conversation.getAccountUuid());
if (account != null) {
conversation.setAccount(account);
} else {
Log.e(Config.LOGTAG, "unable to restore Conversations with " + conversation.getJid());
iterator.remove();
}
}
long diffConversationsRestore = SystemClock.elapsedRealtime() - startTimeConversationsRestore;
Log.d(Config.LOGTAG, "finished restoring conversations in " + diffConversationsRestore + "ms");
Runnable runnable = () -> {
if (DatabaseBackend.requiresMessageIndexRebuild()) {
DatabaseBackend.getInstance(this).rebuildMessagesIndex();
}
final long deletionDate = getAutomaticMessageDeletionDate();
mLastExpiryRun.set(SystemClock.elapsedRealtime());
if (deletionDate > 0) {
Log.d(Config.LOGTAG, "deleting messages that are older than " + AbstractGenerator.getTimestamp(deletionDate));
databaseBackend.expireOldMessages(deletionDate);
}
Log.d(Config.LOGTAG, "restoring roster...");
for (Account account : accounts) {
databaseBackend.readRoster(account.getRoster());
// roster needs to be loaded at this stage
account.initAccountServices(XmppConnectionService.this);
}
getBitmapCache().evictAll();
loadPhoneContacts();
Log.d(Config.LOGTAG, "restoring messages...");
final long startMessageRestore = SystemClock.elapsedRealtime();
final Conversation quickLoad = QuickLoader.get(this.conversations);
if (quickLoad != null) {
restoreMessages(quickLoad);
updateConversationUi();
final long diffMessageRestore = SystemClock.elapsedRealtime() - startMessageRestore;
Log.d(Config.LOGTAG, "quickly restored " + quickLoad.getName() + " after " + diffMessageRestore + "ms");
}
for (Conversation conversation : this.conversations) {
if (quickLoad != conversation) {
restoreMessages(conversation);
}
}
mNotificationService.finishBacklog(false);
restoredFromDatabaseLatch.countDown();
final long diffMessageRestore = SystemClock.elapsedRealtime() - startMessageRestore;
Log.d(Config.LOGTAG, "finished restoring messages in " + diffMessageRestore + "ms");
updateConversationUi();
};
// will contain one write command (expiry) but that's fine
mDatabaseReaderExecutor.execute(runnable);
}
}
use of eu.siacs.conversations.entities.Conversation in project Conversations by siacs.
the class XmppConnectionService method persistSelfNick.
public void persistSelfNick(MucOptions.User self) {
final Conversation conversation = self.getConversation();
final boolean tookProposedNickFromBookmark = conversation.getMucOptions().isTookProposedNickFromBookmark();
Jid full = self.getFullJid();
if (!full.equals(conversation.getJid())) {
Log.d(Config.LOGTAG, "nick changed. updating");
conversation.setContactJid(full);
databaseBackend.updateConversation(conversation);
}
final Bookmark bookmark = conversation.getBookmark();
final String bookmarkedNick = bookmark == null ? null : bookmark.getNick();
if (bookmark != null && (tookProposedNickFromBookmark || TextUtils.isEmpty(bookmarkedNick)) && !full.getResource().equals(bookmarkedNick)) {
final Account account = conversation.getAccount();
final String defaultNick = MucOptions.defaultNick(account);
if (TextUtils.isEmpty(bookmarkedNick) && full.getResource().equals(defaultNick)) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": do not overwrite empty bookmark nick with default nick for " + conversation.getJid().asBareJid());
return;
}
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": persist nick '" + full.getResource() + "' into bookmark for " + conversation.getJid().asBareJid());
bookmark.setNick(full.getResource());
createBookmark(bookmark.getAccount(), bookmark);
}
}
Aggregations