diff --git a/CHANGELOG-upstream.md b/CHANGELOG-upstream.md index cc57f3942..ac01a7a04 100644 --- a/CHANGELOG-upstream.md +++ b/CHANGELOG-upstream.md @@ -9,6 +9,11 @@ * New icon for the in-chat apps button * Improve hint for app drafts * Add Text-To-Speech (TTS) support for in-chat apps +* New icon for the QR icon +* Improved separation between unencryted chats/contacts and encrypted ones, avoiding mixing of encrypted and unencrypted messages in the same chat +* Removed padlocks, as encrypted is the default "normal" state. Instead, unencrypted email is marked with a small email / letter (✉️) icon +* Classic email chats/threads get a big email / letter icon making it easy to recognize +* Update to core 2.0.0 ## v1.58.4 2025-05 diff --git a/jni/dc_wrapper.c b/jni/dc_wrapper.c index 94d13c50a..cc67ba226 100644 --- a/jni/dc_wrapper.c +++ b/jni/dc_wrapper.c @@ -1237,6 +1237,12 @@ JNIEXPORT jint Java_com_b44t_messenger_DcChat_getColor(JNIEnv *env, jobject obj) } +JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isEncrypted(JNIEnv *env, jobject obj) +{ + return dc_chat_is_encrypted(get_dc_chat(env, obj))!=0; +} + + JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isUnpromoted(JNIEnv *env, jobject obj) { return dc_chat_is_unpromoted(get_dc_chat(env, obj))!=0; @@ -1856,6 +1862,12 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcContact_isVerified(JNIEnv *env, job } +JNIEXPORT jboolean Java_com_b44t_messenger_DcContact_isKeyContact(JNIEnv *env, jobject obj) +{ + return dc_contact_is_key_contact(get_dc_contact(env, obj))==1; +} + + JNIEXPORT jint Java_com_b44t_messenger_DcContact_getVerifierId(JNIEnv *env, jobject obj) { return dc_contact_get_verifier_id(get_dc_contact(env, obj)); diff --git a/jni/deltachat-core-rust b/jni/deltachat-core-rust index eed68ceb2..6cc69dca3 160000 --- a/jni/deltachat-core-rust +++ b/jni/deltachat-core-rust @@ -1 +1 @@ -Subproject commit eed68ceb2169707b5e12a119f7dbf91dd24666cc +Subproject commit 6cc69dca31ba8fbe79de5a6f22c5ae63e0bf0ada diff --git a/src/main/java/com/b44t/messenger/DcChat.java b/src/main/java/com/b44t/messenger/DcChat.java index fa40117aa..7dee839b8 100644 --- a/src/main/java/com/b44t/messenger/DcChat.java +++ b/src/main/java/com/b44t/messenger/DcChat.java @@ -38,6 +38,7 @@ public class DcChat { public native String getMailinglistAddr(); public native String getProfileImage (); public native int getColor (); + public native boolean isEncrypted (); public native boolean isUnpromoted (); public native boolean isSelfTalk (); public native boolean isDeviceTalk (); diff --git a/src/main/java/com/b44t/messenger/DcContact.java b/src/main/java/com/b44t/messenger/DcContact.java index 13f55f993..e21b32406 100644 --- a/src/main/java/com/b44t/messenger/DcContact.java +++ b/src/main/java/com/b44t/messenger/DcContact.java @@ -57,6 +57,7 @@ public class DcContact { public native boolean wasSeenRecently(); public native boolean isBlocked (); public native boolean isVerified (); + public native boolean isKeyContact (); public native int getVerifierId (); public native boolean isBot (); diff --git a/src/main/java/com/b44t/messenger/rpc/Rpc.java b/src/main/java/com/b44t/messenger/rpc/Rpc.java index 3eb340889..77e4615e9 100644 --- a/src/main/java/com/b44t/messenger/rpc/Rpc.java +++ b/src/main/java/com/b44t/messenger/rpc/Rpc.java @@ -173,6 +173,10 @@ public class Rpc { } } + public String getMigrationError(int accountId) throws RpcException { + return gson.fromJson(getResult("get_migration_error", accountId), String.class); + } + private static class Response { public final int id; public final JsonElement result; diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java index 05dab7c5f..2bf71cdfa 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java @@ -452,7 +452,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } if (isMultiUser()) { - if (dcChat.canSend() && !dcChat.isBroadcast() && !dcChat.isMailingList()) { + if (dcChat.isEncrypted() + && dcChat.canSend() + && !dcChat.isBroadcast() + && !dcChat.isMailingList()) { menu.findItem(R.id.menu_leave).setVisible(true); } } diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java b/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java index 3771a4cb4..91c72384d 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java @@ -198,6 +198,8 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit refresh(); if (BuildConfig.DEBUG) checkNdkArchitecture(); + + DcHelper.maybeShowMigrationError(this); } /** diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationTitleView.java b/src/main/java/org/thoughtcrime/securesms/ConversationTitleView.java index 729cbb83c..09170384d 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationTitleView.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationTitleView.java @@ -111,7 +111,7 @@ public class ConversationTitleView extends RelativeLayout { else { DcContact dcContact = dcContext.getContact(chatContacts[0]); isOnline = dcContact.wasSeenRecently(); - if (profileView) { + if (profileView || !dcChat.isEncrypted()) { subtitleStr = dcContact.getAddr(); } else if (dcContact.isBot()) { subtitleStr = context.getString(R.string.bot); diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileActivity.java b/src/main/java/org/thoughtcrime/securesms/ProfileActivity.java index 78d637510..506e49e42 100644 --- a/src/main/java/org/thoughtcrime/securesms/ProfileActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ProfileActivity.java @@ -107,7 +107,8 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity boolean canReceive = true; if (chatId != 0) { - menu.findItem(R.id.menu_clone).setVisible(chatIsMultiUser && !chatIsMailingList); + DcChat dcChat = dcContext.getChat(chatId); + menu.findItem(R.id.menu_clone).setVisible(chatIsMultiUser && !chatIsMailingList && dcChat.isEncrypted()); if (chatIsDeviceTalk) { menu.findItem(R.id.edit_name).setVisible(false); menu.findItem(R.id.show_encr_info).setVisible(false); @@ -117,8 +118,9 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity if (chatIsBroadcast) { canReceive = false; } else { - DcChat dcChat = dcContext.getChat(chatId); - if (!chatIsMailingList && !dcChat.canSend()) { + if (!dcChat.isEncrypted() + || !dcChat.canSend() + || chatIsMailingList) { menu.findItem(R.id.edit_name).setVisible(false); } } diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileAdapter.java b/src/main/java/org/thoughtcrime/securesms/ProfileAdapter.java index 02135bb50..e2ecd0595 100644 --- a/src/main/java/org/thoughtcrime/securesms/ProfileAdapter.java +++ b/src/main/java/org/thoughtcrime/securesms/ProfileAdapter.java @@ -277,7 +277,6 @@ public class ProfileAdapter extends RecyclerView.Adapter itemDataStatusText = ""; isBroadcast = dcChat != null && dcChat.isBroadcast(); boolean isMailingList = dcChat != null && dcChat.isMailingList(); - boolean isGroup = dcChat != null && dcChat.getType() == DcChat.DC_CHAT_TYPE_GROUP; boolean isSelfTalk = dcChat != null && dcChat.isSelfTalk(); boolean isDeviceTalk = dcChat != null && dcChat.isDeviceTalk(); memberCount = memberList!=null ? memberList.length : 0; @@ -309,10 +308,10 @@ public class ProfileAdapter extends RecyclerView.Adapter itemData.add(new ItemData(ITEM_LAST_SEEN, lastSeenTxt, 0)); } - if (memberList!=null) { + if (memberList!=null && !isMailingList) { itemData.add(new ItemData(ITEM_DIVIDER, null, 0)); if (dcChat != null) { - if (!isMailingList && dcChat.canSend()) { + if (dcChat.canSend() && dcChat.isEncrypted()) { itemData.add(new ItemData(ITEM_MEMBERS, DcContact.DC_CONTACT_ID_ADD_MEMBER, 0)); if (!isBroadcast) { itemData.add(new ItemData(ITEM_MEMBERS, DcContact.DC_CONTACT_ID_QR_INVITE, 0)); @@ -345,8 +344,6 @@ public class ProfileAdapter extends RecyclerView.Adapter if (dcContact != null) { itemData.add(new ItemData(ITEM_ADDRESS, dcContact.getAddr(), 0)); - } else if (isMailingList) { - itemData.add(new ItemData(ITEM_ADDRESS, dcChat.getMailinglistAddr(), 0)); } } diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileAvatarItem.java b/src/main/java/org/thoughtcrime/securesms/ProfileAvatarItem.java index 301c25fdd..4c70f81fc 100644 --- a/src/main/java/org/thoughtcrime/securesms/ProfileAvatarItem.java +++ b/src/main/java/org/thoughtcrime/securesms/ProfileAvatarItem.java @@ -58,7 +58,7 @@ public class ProfileAvatarItem extends LinearLayout implements RecipientModified greenCheckmark = dcChat.isProtected(); if (dcChat.isMailingList()) { - subtitle = getContext().getString(R.string.contacts_headline); + subtitle = dcChat.getMailinglistAddr(); } else if (dcChat.isBroadcast()) { subtitle = getContext().getResources().getQuantityString(R.plurals.n_recipients, memberCount, memberCount); } else if (dcChat.getType() == DcChat.DC_CHAT_TYPE_GROUP) { diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileFragment.java b/src/main/java/org/thoughtcrime/securesms/ProfileFragment.java index 6a58ea974..e3a665e5b 100644 --- a/src/main/java/org/thoughtcrime/securesms/ProfileFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/ProfileFragment.java @@ -158,7 +158,7 @@ public class ProfileFragment extends Fragment if (contactId>DcContact.DC_CONTACT_ID_LAST_SPECIAL || contactId==DcContact.DC_CONTACT_ID_SELF) { if (actionMode==null) { DcChat dcChat = dcContext.getChat(chatId); - if (dcChat.canSend()) { + if (dcChat.canSend() && dcChat.isEncrypted()) { adapter.toggleMemberSelection(contactId); actionMode = ((AppCompatActivity) requireActivity()).startSupportActionMode(actionModeCallback); } diff --git a/src/main/java/org/thoughtcrime/securesms/WelcomeActivity.java b/src/main/java/org/thoughtcrime/securesms/WelcomeActivity.java index ab51ff950..4b5d5cc7d 100644 --- a/src/main/java/org/thoughtcrime/securesms/WelcomeActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/WelcomeActivity.java @@ -80,6 +80,8 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen intent.setAction(DC_REQUEST_ACCOUNT_DATA); sendBroadcast(intent); } + + DcHelper.maybeShowMigrationError(this); } protected void initializeActionBar() { diff --git a/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java b/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java index 9b354fb53..8caa030f8 100644 --- a/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java +++ b/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java @@ -22,7 +22,7 @@ public class ConversationItemFooter extends LinearLayout { private TextView dateView; private TextView editedView; private ImageView bookmarkIndicatorView; - private ImageView secureIndicatorView; + private ImageView emailIndicatorView; private ImageView locationIndicatorView; private DeliveryStatusView deliveryStatusView; private Integer textColor = null; @@ -48,7 +48,7 @@ public class ConversationItemFooter extends LinearLayout { dateView = findViewById(R.id.footer_date); editedView = findViewById(R.id.footer_edited); bookmarkIndicatorView = findViewById(R.id.footer_bookmark_indicator); - secureIndicatorView = findViewById(R.id.footer_secure_indicator); + emailIndicatorView = findViewById(R.id.footer_email_indicator); locationIndicatorView = findViewById(R.id.footer_location_indicator); deliveryStatusView = new DeliveryStatusView(findViewById(R.id.delivery_indicator)); @@ -65,7 +65,14 @@ public class ConversationItemFooter extends LinearLayout { boolean bookmark = messageRecord.getOriginalMsgId() != 0 || messageRecord.getSavedMsgId() != 0; bookmarkIndicatorView.setVisibility(bookmark ? View.VISIBLE : View.GONE); editedView.setVisibility(messageRecord.isEdited() ? View.VISIBLE : View.GONE); - secureIndicatorView.setVisibility(messageRecord.isSecure() ? View.VISIBLE : View.GONE); + + int downloadState = messageRecord.getDownloadState(); + if (messageRecord.isSecure() || downloadState == DcMsg.DC_DOWNLOAD_AVAILABLE || downloadState == DcMsg.DC_DOWNLOAD_FAILURE || downloadState == DcMsg.DC_DOWNLOAD_IN_PROGRESS) { + emailIndicatorView.setVisibility(View.GONE); + } else { + emailIndicatorView.setVisibility(View.VISIBLE); + } + locationIndicatorView.setVisibility(messageRecord.hasLocation() ? View.VISIBLE : View.GONE); presentDeliveryStatus(messageRecord); } @@ -74,7 +81,7 @@ public class ConversationItemFooter extends LinearLayout { dateView.setTextColor(color); editedView.setTextColor(color); bookmarkIndicatorView.setColorFilter(color); - secureIndicatorView.setColorFilter(color); + emailIndicatorView.setColorFilter(color); locationIndicatorView.setColorFilter(color); deliveryStatusView.setTint(color); } diff --git a/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java b/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java index c036830da..f4c944032 100644 --- a/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java +++ b/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java @@ -23,6 +23,7 @@ import com.b44t.messenger.DcContext; import com.b44t.messenger.DcLot; import com.b44t.messenger.DcMsg; import com.b44t.messenger.rpc.Rpc; +import com.b44t.messenger.rpc.RpcException; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.BuildConfig; @@ -36,6 +37,7 @@ import org.thoughtcrime.securesms.qr.QrActivity; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.FileUtils; import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.Util; import java.io.File; import java.util.Date; @@ -513,4 +515,62 @@ public class DcHelper { if (section != null) { intent.putExtra(LocalHelpActivity.SECTION_EXTRA, section); } context.startActivity(intent); } + + /** + * For the PGP-Contacts migration, things can go wrong. + * The migration happens when the account is setup, at which point no events can be sent yet. + * So, instead, if something goes wrong, it's returned by getLastError(). + * This function shows the error message to the user. + *
+ * A few releases after the PGP-contacts migration (which happened in 2025-05),
+ * we can remove this function again.
+ */
+ public static void maybeShowMigrationError(Context context) {
+ try {
+ String lastError = DcHelper.getRpc(context).getMigrationError(DcHelper.getContext(context).getAccountId());
+
+ if (lastError != null && !lastError.isEmpty()) {
+ Log.w(TAG, "Opening account failed, trying to share error: " + lastError);
+
+ String subject = "Delta Chat failed to update";
+ String email = "delta@merlinux.eu";
+
+ new AlertDialog.Builder(context)
+ .setMessage(context.getString(R.string.error_x, lastError))
+ .setNeutralButton(R.string.global_menu_edit_copy_desktop, (d, which) -> {
+ Util.writeTextToClipboard(context, lastError);
+ })
+ .setPositiveButton(R.string.menu_send, (d, which) -> {
+ Intent sharingIntent = new Intent(
+ Intent.ACTION_SENDTO, Uri.fromParts(
+ "mailto", email, null
+ )
+ );
+ sharingIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{email});
+ sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
+ sharingIntent.putExtra(Intent.EXTRA_TEXT, lastError);
+
+ if (sharingIntent.resolveActivity(context.getPackageManager()) == null) {
+ Log.w(TAG, "No email client found to send crash report");
+ sharingIntent = new Intent(Intent.ACTION_SEND);
+ sharingIntent.setType("text/plain");
+ sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
+ sharingIntent.putExtra(Intent.EXTRA_TEXT, lastError);
+ sharingIntent.putExtra(Intent.EXTRA_EMAIL, email);
+ }
+
+ Intent chooser =
+ Intent.createChooser(sharingIntent, "Send using...");
+ chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ chooser.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+
+ context.startActivity(chooser);
+ })
+ .setCancelable(false)
+ .show();
+ }
+ } catch (RpcException e) {
+ e.printStackTrace();
+ }
+ }
}
diff --git a/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java b/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java
index 1ab06a1b5..c60ea4b7f 100644
--- a/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java
+++ b/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java
@@ -80,7 +80,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientM
this.nameView.setTypeface(null, Typeface.NORMAL);
}
if (specialId == DcContact.DC_CONTACT_ID_QR_INVITE) {
- this.avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.baseline_qr_code_24).asDrawable(getContext(), ThemeUtil.getDummyContactColor(getContext())));
+ this.avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_qr_code_24).asDrawable(getContext(), ThemeUtil.getDummyContactColor(getContext())));
} else {
this.avatar.setAvatar(glideRequests, recipient, false);
}
diff --git a/src/main/java/org/thoughtcrime/securesms/qr/BackupReceiverFragment.java b/src/main/java/org/thoughtcrime/securesms/qr/BackupReceiverFragment.java
index 1dfb677b4..9dc659633 100644
--- a/src/main/java/org/thoughtcrime/securesms/qr/BackupReceiverFragment.java
+++ b/src/main/java/org/thoughtcrime/securesms/qr/BackupReceiverFragment.java
@@ -80,6 +80,7 @@ public class BackupReceiverFragment extends Fragment implements DcEventCenter.Dc
Log.i(TAG,"DC_EVENT_IMEX_PROGRESS, " + permille);
if (permille == 0) {
+ DcHelper.maybeShowMigrationError(getTransferActivity());
getTransferActivity().setTransferError("Receiving Error");
} else if (permille < 1000) {
percent = permille/10;
diff --git a/src/main/res/drawable-hdpi/msg_encr_out.png b/src/main/res/drawable-hdpi/msg_encr_out.png
deleted file mode 100644
index af0aeee91..000000000
Binary files a/src/main/res/drawable-hdpi/msg_encr_out.png and /dev/null differ
diff --git a/src/main/res/drawable-mdpi/msg_encr_out.png b/src/main/res/drawable-mdpi/msg_encr_out.png
deleted file mode 100644
index 760538192..000000000
Binary files a/src/main/res/drawable-mdpi/msg_encr_out.png and /dev/null differ
diff --git a/src/main/res/drawable-xhdpi/msg_encr_out.png b/src/main/res/drawable-xhdpi/msg_encr_out.png
deleted file mode 100644
index 2e94856eb..000000000
Binary files a/src/main/res/drawable-xhdpi/msg_encr_out.png and /dev/null differ
diff --git a/src/main/res/drawable-xxhdpi/msg_encr_out.png b/src/main/res/drawable-xxhdpi/msg_encr_out.png
deleted file mode 100644
index 758198e40..000000000
Binary files a/src/main/res/drawable-xxhdpi/msg_encr_out.png and /dev/null differ
diff --git a/src/main/res/drawable/baseline_qr_code_24.xml b/src/main/res/drawable/baseline_qr_code_24.xml
deleted file mode 100644
index e7edb465f..000000000
--- a/src/main/res/drawable/baseline_qr_code_24.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-