Compare commits

...

37 Commits

Author SHA1 Message Date
adbenitez eed844469b apply spotless 2026-06-15 23:21:12 +02:00
adbenitez 59ceae46a0 Merge remote-tracking branch 'upstream/main' 2026-06-15 23:05:19 +02:00
adbenitez 693188dcff Merge remote-tracking branch 'upstream/main' 2026-06-15 23:04:36 +02:00
adb 025ff086c8 Merge pull request #4487 from deltachat/prep-2.53.0
prepare 2.53.0
2026-06-15 22:47:25 +02:00
adb a802bfcb61 Merge branch 'main' into prep-2.53.0 2026-06-15 22:46:19 +02:00
adbenitez 1f18bfe2dd prepare 2.53.0 2026-06-15 22:28:18 +02:00
adb dc1bc77925 Merge pull request #4486 from deltachat/update-core-and-stuff-2026-06-15
Update core to 2.53.0
2026-06-15 22:24:28 +02:00
adbenitez ac6383d79a update changelog 2026-06-15 22:10:55 +02:00
adbenitez 59fec8462c update translations 2026-06-15 22:03:27 +02:00
adbenitez d64baa294f update deltachat-core-rust to 'chore(release): prepare for 2.53.0' of 'v2.53.0' 2026-06-15 21:59:16 +02:00
adb e7e14af158 Merge pull request #4482 from deltachat/adb/issue-4481
don't create mailto unencrypted chat if force-encryption is enabled
2026-06-14 01:57:19 +02:00
adb e094e96669 Merge branch 'main' into adb/issue-4481 2026-06-14 01:54:47 +02:00
adb fa56e8ca0d Merge pull request #4483 from deltachat/adb/fix-ci-for-outside-pr
don't try to upload artifacts for PRs from external contributors
2026-06-12 23:15:56 +02:00
wch423 3d5abdcd79 Correct CHANGELOG.md 2026-06-12 16:21:38 +02:00
wchen342 92c4e41b59 Call maybeNetwork() on network unblock (#4476)
Limit maybeNetwork() calls when network changes

* Call maybeNetwork() on network unblock to allow immediate fetch during Doze (API 29+)
* Restrict maybeNetwork() in onAvailable() to API 24-28
2026-06-12 14:11:10 +00:00
adbenitez bbfd0c31da Merge remote-tracking branch 'upstream/main' 2026-06-11 20:19:42 +02:00
adb d5f627be50 Merge pull request #4461 from d2weber/notif_msg_style
feat: use message style notifications
2026-06-11 20:18:38 +02:00
adbenitez f0ca58d53f don't try to upload artifacts for PRs from external contributors 2026-06-11 20:00:35 +02:00
adb 56f5655693 Merge branch 'main' into notif_msg_style 2026-06-11 19:44:16 +02:00
wchen342 310a7f99c8 Remove media notification when audio playback ends naturally (#4472) 2026-06-11 19:25:17 +02:00
adb d0f7fca8c9 Merge branch 'main' into adb/issue-4481 2026-06-11 19:05:27 +02:00
B. Petersen 80de08f980 remove remainings of is_chatmail usage
instead, force_ncryption is checked now,
and this only for for option "New Chat -> New Email".

this also fixes the bug that, for classic relays,
by default "force encryption" is set but we still show "new email".

what's left for now is a check for is_chatmail whether fcm is sufficient or not.
2026-06-11 19:01:50 +02:00
adb 80a0ff0098 Merge pull request #4473 from deltachat/adb/ci-update-ndk-30
update NDK version in CI workflows
2026-06-11 18:53:22 +02:00
adbenitez 9c55de17a3 don't create mailto unencrypted chat if force-encryption is enabled 2026-06-11 18:49:08 +02:00
d2weber 8c1bc8e70e apply messaging style also if !isDisplayMessage() 2026-06-10 10:25:58 +02:00
d2weber f0760d6695 make notification icon nice and big in all cases 2026-06-09 20:47:26 +02:00
adb 6d038e9d7f Merge branch 'main' into adb/ci-update-ndk-30 2026-06-09 18:12:23 +02:00
adbenitez 46be278bf5 update ndk version 2026-06-09 17:58:01 +02:00
wchen342 1577b90047 Merge branch 'main' into notif_msg_style 2026-06-05 20:04:12 +02:00
d2weber 515a84b161 Merge branch 'main' into notif_msg_style 2026-06-04 20:04:12 +02:00
d2weber d757ef59c0 fixup: displayContact without displayMessage 2026-06-04 18:12:03 +02:00
d2weber d0a584d15d fixup: cleanup getAvatar 2026-06-04 11:36:53 +02:00
d2weber f8ec6d2da2 fixup: reactions and webxdc updates 2026-06-04 11:09:24 +02:00
d2weber c3b7e06b86 add isBot to person (not sure if it has any effect) 2026-06-03 22:16:05 +02:00
d2weber d5edcea5d0 fixup: convert timestamp to ms 2026-06-03 22:07:32 +02:00
d2weber 8a3d29fca3 fixup: handle null from getAvatar() 2026-06-03 22:06:15 +02:00
d2weber 6156ee0534 feat: use message style notifications
With MessagingStyle, Notifications can be expanded to show more text of
the message than just a single line.

This feature has been requested in
https://support.delta.chat/t/swipe-down-on-notification-to-expand-it/2742
2026-06-03 19:53:21 +02:00
21 changed files with 633 additions and 269 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
- uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1.6.0
id: setup-ndk
with:
ndk-version: r27
ndk-version: "r29"
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
+3 -1
View File
@@ -45,7 +45,7 @@ jobs:
- uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1.6.0
id: setup-ndk
with:
ndk-version: r27
ndk-version: "r29"
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
@@ -77,12 +77,14 @@ jobs:
- name: Upload APK
id: upload
if: github.event.pull_request.head.repo.full_name == github.repository
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: app-preview.apk
path: 'build/outputs/apk/foss/debug/*.apk'
- name: Add artifact links to PR
if: github.event.pull_request.head.repo.full_name == github.repository
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
ARTIFACT_URL: ${{ steps.upload.outputs.artifact-url }}
+11 -1
View File
@@ -1,5 +1,15 @@
# Delta Chat Android Changelog
## v2.53.0
2026-06
* Use message style notifications for longer message previews
* Remove notification after audio playback ends
* Fix: do not allow blocked contacts to use our invite links
* Fix sending mini-app that was used/prepared before sending
* Some more small fixes and updated translations
* Update to core 2.53.0
## v2.52.0
2026-06
@@ -7,7 +17,7 @@
* Fix: Incorrect total time when attaching audio files as draft
* Fix: Audio files in draft showing total time from wrong file
* Fix: Update the channel title after joining if the QR code had an outdated title
* Voice recording will be automatically saved as draft when interrupted
* Voice recording will be automatically saved as draft when interrupted
* Update to core 2.52.0
## v2.51.0
+2 -2
View File
@@ -34,8 +34,8 @@ android {
useLibrary 'org.apache.http.legacy'
defaultConfig {
versionCode 30000745
versionName "2.52.0"
versionCode 30000746
versionName "2.53.0"
applicationId "chat.delta.lite"
multiDexEnabled true
@@ -84,7 +84,7 @@ public class DcAccounts {
public boolean isAllChatmail() {
for (int accountId : getAll()) {
DcContext dcContext = getAccount(accountId);
if (!dcContext.isChatmail()) {
if (dcContext.getConfigInt("is_chatmail") == 0) {
return false;
}
}
@@ -362,10 +362,6 @@ public class DcContext {
return displayname;
}
public boolean isChatmail() {
return getConfigInt("is_chatmail") == 1;
}
public boolean isMuted() {
return getConfigInt("is_muted") == 1;
}
@@ -302,7 +302,10 @@ public class ApplicationContext extends MultiDexApplication {
Log.i(
"DeltaChat",
"++++++++++++++++++ NetworkCallback.onAvailable() #" + debugOnAvailableCount++);
getDcAccounts().maybeNetwork();
// onBlockedStatusChanged is only available on API 29+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
getDcAccounts().maybeNetwork();
}
}
@Override
@@ -310,8 +313,13 @@ public class ApplicationContext extends MultiDexApplication {
@NonNull android.net.Network network, boolean blocked) {
Log.i(
"DeltaChat",
"++++++++++++++++++ NetworkCallback.onBlockedStatusChanged() #"
"++++++++++++++++++ NetworkCallback.onBlockedStatusChanged("
+ blocked
+ ") #"
+ debugOnBlockedStatusChangedCount++);
if (!blocked) {
getDcAccounts().maybeNetwork();
}
}
@Override
@@ -260,12 +260,11 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
final String addr = extraEmail[0];
int contactId = dcContext.lookupContactIdByAddr(addr);
if (contactId == 0) {
contactId = dcContext.createContact(null, addr);
if (contactId != 0 || dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 0) {
if (contactId == 0) contactId = dcContext.createContact(null, addr);
chatId = dcContext.createChatByContactId(contactId);
accId = dcContext.getAccountId();
}
chatId = dcContext.createChatByContactId(contactId);
accId = dcContext.getAccountId();
}
Intent composeIntent;
if (accId != -1 && chatId > 0) {
@@ -51,7 +51,6 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.json.JSONObject;
import org.thoughtcrime.securesms.connect.AccountManager;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.util.IntentUtils;
@@ -264,7 +264,10 @@ public class AudioPlaybackViewModel extends ViewModel {
updateCurrentState(false);
} else if (player.getPlaybackState() == Player.STATE_ENDED
&& !player.hasNextMediaItem()) {
mediaController.setPlayWhenReady(false);
mediaController.stop();
mediaController.clearMediaItems();
stopUpdateProgress();
playbackState.setValue(AudioPlaybackState.idle());
}
}
}
@@ -46,7 +46,7 @@ public class DcContactsLoader extends AsyncLoader<DcContactsLoader.Ret> {
if (query == null && addScanQRLink) {
additional_items = Util.appendInt(additional_items, DcContact.DC_CONTACT_ID_QR_INVITE);
}
if (addCreateContactLink && !dcContext.isChatmail()) {
if (addCreateContactLink && dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 0) {
additional_items =
Util.appendInt(additional_items, DcContact.DC_CONTACT_ID_NEW_CLASSIC_CONTACT);
}
@@ -54,7 +54,7 @@ public class DcContactsLoader extends AsyncLoader<DcContactsLoader.Ret> {
additional_items = Util.appendInt(additional_items, DcContact.DC_CONTACT_ID_NEW_GROUP);
additional_items = Util.appendInt(additional_items, DcContact.DC_CONTACT_ID_NEW_BROADCAST);
if (!dcContext.isChatmail()) {
if (dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 0) {
additional_items =
Util.appendInt(additional_items, DcContact.DC_CONTACT_ID_NEW_UNENCRYPTED_GROUP);
}
@@ -528,16 +528,20 @@ public class DcHelper {
.show();
}
public static void showInvalidUnencryptedDialog(Context context) {
new AlertDialog.Builder(context)
public static AlertDialog.Builder prepareInvalidUnencryptedDialog(
Context context, AlertDialog.Builder builder) {
return builder
.setMessage(context.getString(R.string.invalid_unencrypted_explanation))
.setNeutralButton(R.string.learn_more, (d, w) -> openHelp(context, "#howtoe2ee"))
.setNegativeButton(
R.string.qrscan_title,
(d, w) -> context.startActivity(new Intent(context, QrActivity.class)))
.setPositiveButton(R.string.ok, null)
.setCancelable(true)
.show();
.setCancelable(true);
}
public static void showInvalidUnencryptedDialog(Context context) {
prepareInvalidUnencryptedDialog(context, new AlertDialog.Builder(context)).show();
}
public static void openHelp(Context context, String section) {
@@ -11,6 +11,7 @@ import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.AudioAttributes;
import android.media.RingtoneManager;
@@ -23,13 +24,18 @@ import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.app.Person;
import androidx.core.app.RemoteInput;
import androidx.core.app.TaskStackBuilder;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.drawable.IconCompat;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMsg;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.HashMap;
@@ -42,6 +48,7 @@ import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.ConversationActivity;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.ShareActivity;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference;
@@ -61,8 +68,18 @@ public class NotificationCenter {
private volatile long lastAudibleNotification = 0;
private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(2);
// Map<accountId, Map<chatId, lines>, contains the last lines of each chat for each account
private final HashMap<Integer, HashMap<Integer, LinkedHashMap<Integer, String>>> inboxes =
private static class NotifData {
final Person sender;
final String text;
NotifData(Person sender, String text) {
this.sender = sender;
this.text = text;
}
}
// notification history of each chat for each account
private final HashMap<Integer, HashMap<Integer, LinkedHashMap<Integer, NotifData>>> inboxes =
new HashMap<>();
public NotificationCenter(Context context) {
@@ -410,31 +427,36 @@ public class NotificationCenter {
DcMsg dcMsg = dcContext.getMsg(msgId);
NotificationPrivacyPreference privacy = Prefs.getNotificationPrivacy(context);
String shortLine =
DcContact sender = dcContext.getContact(dcMsg.getFromId());
String senderName = dcMsg.getSenderName(sender);
String text =
privacy.isDisplayMessage()
? dcMsg.getSummarytext(2000)
: context.getString(R.string.notify_new_message);
String shortLine = text;
if (dcChat.isMultiUser() && privacy.isDisplayContact()) {
shortLine =
dcMsg.getSenderName(dcContext.getContact(dcMsg.getFromId())) + ": " + shortLine;
shortLine = senderName + ": " + text;
}
String tickerLine = shortLine;
if (!dcChat.isMultiUser() && privacy.isDisplayContact()) {
tickerLine =
dcMsg.getSenderName(dcContext.getContact(dcMsg.getFromId())) + ": " + tickerLine;
if (dcMsg.getOverrideSenderName() != null) {
// There is an "overridden" display name on the message, so, we need to prepend the
// display name to the message,
// i.e. set the shortLine to be the same as the tickerLine.
shortLine = tickerLine;
}
NotifData notifData =
new NotifData(
new Person.Builder()
.setName(senderName)
.setIcon(getAvatarIcon(sender))
.setBot(sender.isBot())
.setKey(accountId + "-" + sender.getId())
.build(),
text);
String tickerLine = text;
if (privacy.isDisplayContact()) {
tickerLine = senderName + ": " + text;
}
DcMsg quotedMsg = dcMsg.getQuotedMsg();
boolean isMention = dcChat.isMultiUser() && quotedMsg != null && quotedMsg.isOutgoing();
maybeAddNotification(accountId, dcChat, msgId, shortLine, tickerLine, true, isMention);
maybeAddNotification(accountId, dcChat, msgId, notifData, tickerLine, true, isMention);
});
}
@@ -451,15 +473,27 @@ public class NotificationCenter {
}
DcContact sender = dcContext.getContact(contactId);
String shortLine =
String senderName = dcMsg.getSenderName(sender);
String text =
context.getString(
R.string.reaction_by_other,
sender.getDisplayName(),
reaction,
dcMsg.getSummarytext(2000));
DcChat dcChat = dcContext.getChat(dcMsg.getChatId());
NotifData notifData =
new NotifData(
new Person.Builder()
.setName(senderName)
.setIcon(getAvatarIcon(sender))
.setBot(sender.isBot())
.setKey(accountId + "-" + sender.getId())
.build(),
text);
maybeAddNotification(
accountId, dcChat, msgId, shortLine, shortLine, false, dcChat.isMultiUser());
accountId, dcChat, msgId, notifData, text, false, dcChat.isMultiUser());
});
}
@@ -473,6 +507,7 @@ public class NotificationCenter {
DcContext dcContext = context.getDcAccounts().getAccount(accountId);
DcMsg dcMsg = dcContext.getMsg(msgId);
DcChat dcChat = dcContext.getChat(dcMsg.getChatId());
DcMsg parentMsg;
if (dcMsg.getType() == DcMsg.DC_MSG_WEBXDC) {
parentMsg = dcMsg;
@@ -486,10 +521,34 @@ public class NotificationCenter {
JSONObject info = parentMsg.getWebxdcInfo();
final String name = JsonUtils.optString(info, "name");
String shortLine = name.isEmpty() ? text : (name + ": " + text);
DcChat dcChat = dcContext.getChat(dcMsg.getChatId());
String tickerLine = name.isEmpty() ? text : (name + ": " + text);
NotifData notifData;
if (dcChat.isMultiUser()) {
byte[] blob = parentMsg.getWebxdcBlob(JsonUtils.optString(info, "icon"));
notifData =
new NotifData(
new Person.Builder()
.setName(name)
.setIcon(getAvatarIcon(blob))
.setKey(accountId + "-webxdc-" + msgId)
.build(),
text);
} else {
DcContact sender = dcContext.getContact(contactId);
notifData =
new NotifData(
new Person.Builder()
.setName(dcMsg.getSenderName(sender))
.setIcon(getAvatarIcon(sender))
.setBot(sender.isBot())
.setKey(dcContext.getAccountId() + "-" + sender.getId())
.build(),
tickerLine);
}
maybeAddNotification(
accountId, dcChat, msgId, shortLine, shortLine, false, dcChat.isMultiUser());
accountId, dcChat, msgId, notifData, tickerLine, false, dcChat.isMultiUser());
});
}
@@ -498,7 +557,7 @@ public class NotificationCenter {
int accountId,
DcChat dcChat,
int msgId,
String shortLine,
NotifData notifData,
String tickerLine,
boolean playInChatSound,
boolean isMention) {
@@ -538,20 +597,23 @@ public class NotificationCenter {
// the user may eg. have chosen a different sound
String notificationChannel = getNotificationChannel(notificationManager, chatData, dcChat);
LinkedHashMap<Integer, String> messagesForInbox = null;
if (privacy.isDisplayContact() && privacy.isDisplayMessage()) {
LinkedHashMap<Integer, NotifData> messagesForInbox = null;
if (privacy.isDisplayContact()) {
synchronized (inboxes) {
HashMap<Integer, LinkedHashMap<Integer, String>> accountInbox = inboxes.get(accountId);
HashMap<Integer, LinkedHashMap<Integer, NotifData>> accountInbox = inboxes.get(accountId);
if (accountInbox == null) {
accountInbox = new HashMap<>();
inboxes.put(accountId, accountInbox);
}
LinkedHashMap<Integer, String> messages = accountInbox.get(chatId);
LinkedHashMap<Integer, NotifData> messages = accountInbox.get(chatId);
if (messages == null) {
messages = new LinkedHashMap<>();
accountInbox.put(chatId, messages);
}
messages.put(msgId, shortLine);
if (!privacy.isDisplayMessage()) {
messages.clear();
}
messages.put(msgId, notifData);
messagesForInbox = new LinkedHashMap<>(messages);
}
}
@@ -564,7 +626,7 @@ public class NotificationCenter {
dcContext,
dcChat,
notificationChannel,
shortLine,
notifData.text,
tickerLine,
signal,
messagesForInbox,
@@ -583,7 +645,7 @@ public class NotificationCenter {
String contentText,
String ticker,
boolean signal,
LinkedHashMap<Integer, String> messagesForInbox,
LinkedHashMap<Integer, NotifData> messagesForInbox,
int messageCount,
boolean includeSummary) {
try {
@@ -694,14 +756,42 @@ public class NotificationCenter {
}
}
// Create inbox style (gets visible if the notification is expanded)
if (privacy.isDisplayContact() && privacy.isDisplayMessage() && messagesForInbox != null) {
// Create messaging style
if (privacy.isDisplayContact() && messagesForInbox != null) {
try {
NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
for (String line : messagesForInbox.values()) {
inboxStyle.addLine(line);
Intent viewChatIntent = new Intent(context, ShareActivity.class);
viewChatIntent.setAction(Intent.ACTION_SEND);
viewChatIntent.putExtra(ShareActivity.EXTRA_ACC_ID, dcContext.getAccountId());
viewChatIntent.putExtra(ShareActivity.EXTRA_CHAT_ID, dcChat.getId());
ShortcutInfoCompat shortcut =
new ShortcutInfoCompat.Builder(
context, "chat-" + dcContext.getAccountId() + "-" + dcChat.getId())
.setShortLabel(dcChat.getName())
.setLongLived(true)
.setIntent(viewChatIntent)
.build();
ShortcutManagerCompat.pushDynamicShortcut(context, shortcut);
builder.setShortcutInfo(shortcut);
DcContact selfContact = dcContext.getContact(DcContact.DC_CONTACT_ID_SELF);
Person self =
new Person.Builder()
.setName(selfContact.getDisplayName())
.setIcon(getAvatarIcon(selfContact))
.setKey(accountId + "-" + selfContact.getId())
.build();
NotificationCompat.MessagingStyle style = new NotificationCompat.MessagingStyle(self);
if (dcChat.isMultiUser()) {
style.setGroupConversation(true);
style.setConversationTitle(dcChat.getName());
}
builder.setStyle(inboxStyle);
for (Map.Entry<Integer, NotifData> msgEntry : messagesForInbox.entrySet()) {
long timestamp_ms = dcContext.getMsg(msgEntry.getKey()).getSortTimestamp() * 1000;
NotifData notifData = msgEntry.getValue();
style.addMessage(notifData.text, timestamp_ms, notifData.sender);
}
builder.setStyle(style);
} catch (Exception e) {
Log.w(TAG, e);
}
@@ -748,7 +838,7 @@ public class NotificationCenter {
@WorkerThread
private void rebuildNotification(
int accountId, int chatId, LinkedHashMap<Integer, String> messages) {
int accountId, int chatId, LinkedHashMap<Integer, NotifData> messages) {
try {
DcContext dcContext = ApplicationContext.getDcAccounts().getAccount(accountId);
DcChat dcChat = dcContext.getChat(chatId);
@@ -766,9 +856,9 @@ public class NotificationCenter {
// Get the latest message ID (last entry in LinkedHashMap)
Integer latestMsgId = null;
String lastLine = null;
for (Map.Entry<Integer, String> entry : messages.entrySet()) {
for (Map.Entry<Integer, NotifData> entry : messages.entrySet()) {
latestMsgId = entry.getKey();
lastLine = entry.getValue();
lastLine = entry.getValue().text;
}
if (latestMsgId == null || lastLine == null) {
return;
@@ -797,8 +887,35 @@ public class NotificationCenter {
}
}
public Bitmap getAvatar(DcChat dcChat) {
Recipient recipient = new Recipient(context, dcChat);
private static @Nullable IconCompat getAvatarIcon(byte[] blob) {
if (blob == null) {
return null;
}
ByteArrayInputStream is = new ByteArrayInputStream(blob);
BitmapDrawable drawable = (BitmapDrawable) Drawable.createFromStream(is, "icon");
Bitmap bitmap = drawable.getBitmap();
return IconCompat.createWithBitmap(bitmap);
}
private @Nullable IconCompat getAvatarIcon(DcChat dcChat) {
Bitmap avatar = getAvatar(dcChat);
return avatar != null ? IconCompat.createWithBitmap(avatar) : null;
}
private @Nullable IconCompat getAvatarIcon(DcContact dcContact) {
Bitmap avatar = getAvatar(dcContact);
return avatar != null ? IconCompat.createWithBitmap(avatar) : null;
}
private @Nullable Bitmap getAvatar(DcChat dcChat) {
return getAvatar(new Recipient(context, dcChat));
}
private @Nullable Bitmap getAvatar(DcContact dcContact) {
return getAvatar(new Recipient(context, dcContact));
}
private @Nullable Bitmap getAvatar(Recipient recipient) {
try {
Drawable drawable;
ContactPhoto contactPhoto = recipient.getContactPhoto(context);
@@ -837,12 +954,12 @@ public class NotificationCenter {
public void removeNotification(int accountId, int chatId, int msgId) {
boolean shouldCancelNotification = false;
boolean removeSummary = false;
LinkedHashMap<Integer, String> remainingMessages = null;
LinkedHashMap<Integer, NotifData> remainingMessages = null;
synchronized (inboxes) {
HashMap<Integer, LinkedHashMap<Integer, String>> accountInbox = inboxes.get(accountId);
HashMap<Integer, LinkedHashMap<Integer, NotifData>> accountInbox = inboxes.get(accountId);
if (accountInbox != null) {
LinkedHashMap<Integer, String> messages = accountInbox.get(chatId);
LinkedHashMap<Integer, NotifData> messages = accountInbox.get(chatId);
if (messages != null) {
messages.remove(msgId);
@@ -875,7 +992,7 @@ public class NotificationCenter {
public void removeNotifications(int accountId, int chatId) {
boolean removeSummary;
synchronized (inboxes) {
HashMap<Integer, LinkedHashMap<Integer, String>> accountInbox = inboxes.get(accountId);
HashMap<Integer, LinkedHashMap<Integer, NotifData>> accountInbox = inboxes.get(accountId);
if (accountInbox == null) {
accountInbox = new HashMap<>();
}
@@ -901,7 +1018,7 @@ public class NotificationCenter {
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
String tag = String.valueOf(accountId);
synchronized (inboxes) {
HashMap<Integer, LinkedHashMap<Integer, String>> accountInbox = inboxes.get(accountId);
HashMap<Integer, LinkedHashMap<Integer, NotifData>> accountInbox = inboxes.get(accountId);
notificationManager.cancel(tag, ID_MSG_SUMMARY);
if (accountInbox != null) {
for (Integer chatId : accountInbox.keySet()) {
@@ -18,6 +18,7 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.b44t.messenger.DcContext;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.google.zxing.BinaryBitmap;
@@ -120,10 +121,12 @@ public class QrActivity extends BaseActionBarActivity implements View.OnClickLis
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
DcContext dcContext = DcHelper.getContext(this);
menu.clear();
getMenuInflater().inflate(R.menu.qr_show, menu);
menu.findItem(R.id.new_classic_contact)
.setVisible(!scanRelay && !DcHelper.getContext(this).isChatmail());
.setVisible(!scanRelay && dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 0);
Util.redMenuItem(menu, R.id.withdraw);
if (tabLayout.getSelectedTabPosition() == TAB_SCAN) {
@@ -314,6 +314,12 @@ public class QrCodeHandler {
private void showFingerprintOrQrSuccess(
AlertDialog.Builder builder, DcLot qrParsed, String name) {
if (qrParsed.getState() == DcContext.DC_QR_ADDR
&& dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 1) {
DcHelper.prepareInvalidUnencryptedDialog(activity, builder);
return;
}
@StringRes
int resId =
qrParsed.getState() == DcContext.DC_QR_ADDR
@@ -57,22 +57,26 @@ public class LongClickCopySpan extends ClickableSpan {
DcContext dcContext = DcHelper.getContext(activity);
int contactId = dcContext.lookupContactIdByAddr(addr);
if (contactId == 0 && dcContext.mayBeValidAddr(addr)) {
contactId = dcContext.createContact(null, addr);
}
DcContact contact = dcContext.getContact(contactId);
if (contact.getId() != 0
DcContact contact = (contactId != 0) ? dcContext.getContact(contactId) : null;
if (contact != null
&& !contact.isBlocked()
&& dcContext.getChatIdByContactId(contact.getId()) != 0) {
openChat(activity, contact);
} else if (contact == null
&& dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 1) {
DcHelper.showInvalidUnencryptedDialog(activity);
} else {
String name = contact != null ? contact.getDisplayName() : addr;
new AlertDialog.Builder(activity)
.setMessage(
activity.getString(R.string.ask_start_chat_with, contact.getDisplayName()))
.setMessage(activity.getString(R.string.ask_start_chat_with, name))
.setPositiveButton(
android.R.string.ok,
(dialog, which) -> {
openChat(activity, contact);
openChat(
activity,
contact == null
? dcContext.getContact(dcContext.createContact(null, addr))
: contact);
})
.setNegativeButton(R.string.cancel, null)
.show();
+1 -1
View File
@@ -272,7 +272,7 @@
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
<string name="channels">Canals</string>
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
<string name="new_channel">Nou Canal</string>
<string name="new_channel">Canal nou</string>
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
<string name="channel_name">Nom del Canal</string>
<!-- The number of times a message is shown in channels. Use what other messengers are using here, smth. as "N-times seen" or "1 Aufruf" (german) work as well, where "1 impression" or "1 hit" may sound too formal and may be more marketing slang. -->
+1 -1
View File
@@ -673,7 +673,7 @@
<string name="welcome_chat_over_email">Sicherer dezentraler Chat</string>
<string name="scan_invitation_code">Einladungscode scannen</string>
<string name="login_title">Login</string>
<string name="login_advanced_hint">Für fortgeschrittene Benutzer:\n\nVerwende keine Adresse, die du bereits in einer anderen App nutzt.</string>
<string name="login_advanced_hint">Für fortgeschrittene Benutzer.\n\nVerwende keine Adresse, die du bereits in einer anderen App nutzt.</string>
<string name="login_inbox">Posteingang</string>
<string name="login_imap_login">IMAP-Anmeldename</string>
<string name="login_imap_server">IMAP-Server</string>
File diff suppressed because it is too large Load Diff
+3
View File
@@ -450,6 +450,7 @@
<string name="call_answered_elsewhere">Chiamata risposta su un altro dispositivo</string>
<string name="call_requires_camera_permission">Per le videochiamate è necessaria l\'autorizzazione per l\'utilizzo della fotocamera.</string>
<string name="call_requires_mic_permission">Per le chiamate è necessaria l\'autorizzazione per l\'utilizzo del microfono.</string>
<string name="call_requires_connection">Impossibile avviare la chiamata. Assicurati che il tuo dispositivo disponga di una connessione a Internet e riprova.</string>
<!-- Used e.g. in a notifications to describe an ongoing call. "Call" is a noun here, in the meaning of "This is the call with ..." where the placeholder is replaced by the name of the contact. -->
<string name="call_with">Chiama con %1$s</string>
<!-- Use e.g. in a notifications during ringing. Placeholder is relaced by the name of the contact being called. -->
@@ -690,6 +691,7 @@
<string name="welcome_chat_over_email">Chat Decentralizzata Sicura</string>
<string name="scan_invitation_code">Scansiona Codice Invito</string>
<string name="login_title">Accedi</string>
<string name="login_advanced_hint">Questo login è riservato agli utenti avanzati.\n\nNon utilizzare un indirizzo che stai già usando in un\'altra app.</string>
<string name="login_inbox">In Arrivo</string>
<string name="login_imap_login">Nome Accesso IMAP</string>
<string name="login_imap_server">Server IMAP</string>
@@ -724,6 +726,7 @@
<string name="used_for_sending">Utilizzato per l\'invio</string>
<string name="hide_from_contacts">Nascondi dai Contatti</string>
<string name="hidden_from_contacts">Nascosto dai contatti</string>
<string name="enforce_e2ee">Applica Crittografia a Tutti i Ripetitori</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">I messaggi vengono ricevuti su tutti i ripetitori.\n\n⚠️ Se modifichi qualcosa qui, assicurati che tutti i tuoi dispositivi abbiano almeno la versione 2.47.0. Altrimenti i dispositivi più vecchi potrebbero non ricevere i messaggi.</string>
<!-- shown if a QR code was scanned that can be used as a relay -->