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 @@ - - - - - - - - - - - - - diff --git a/src/main/res/drawable/ic_outline_email.xml b/src/main/res/drawable/ic_outline_email.xml new file mode 100644 index 000000000..19ab62ba4 --- /dev/null +++ b/src/main/res/drawable/ic_outline_email.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/ic_qr_code_24.xml b/src/main/res/drawable/ic_qr_code_24.xml new file mode 100644 index 000000000..f95c18d61 --- /dev/null +++ b/src/main/res/drawable/ic_qr_code_24.xml @@ -0,0 +1,3 @@ + + + diff --git a/src/main/res/layout/conversation_item_footer.xml b/src/main/res/layout/conversation_item_footer.xml index 43ed98cb0..291493707 100644 --- a/src/main/res/layout/conversation_item_footer.xml +++ b/src/main/res/layout/conversation_item_footer.xml @@ -19,10 +19,10 @@ />