Merge the verified-1:1 commits into main (#2752)

* Verified 1:1 chats, Android UI (#2560)

Depends on https://github.com/deltachat/deltachat-core-rust/pull/4315/.

Follow-up for https://github.com/deltachat/deltachat-android/pull/2541.

- Show a "verified" icon on protection changed update messages
- Add C API
- Show a dialog over the input bar not only for contact requests, but also when the protection is broken (using the new method `isHalfBlocked()`)
  - The positive button reads "OK" (as opposed to contact requests where it reads "Accept")
- Add SVG icons, remove PNG one
- Translations
- When tapping on the `DC_INFO_PROTECTION_{EN|DIS}ABLED` message, show more information (for now, it leads to the online preview of my FAQ PR)
- Block loading remote images in the "Full Msg View" not only for contact requests, also when the protection is broken (using the new method `isHalfBlocked()`)
- Show a big verified/crossed-out-verified symbol over the `DC_INFO_PROTECTION_{EN|DIS}ABLED` messages
- Fix a bug that was kind of present before (for contact requests) but only became really visible now:
  - Set a draft with an image (or other attachment)
  - Your chat partner breaks verification
  - Expected: Both the input bar and the draft image are hidden by the input-bar-dialog.
  - Bug behavior (before c31de5bcf): The input bar is hidden, but the draft image stays visible.

* change button order of verification-broken alert (#2621)

Having "OK" in the middle of the buttons is weird. The old order was:

```
End-to-end encryption cannot...
[More Info]  [OK] [Scan QR code]
```

1. _If_ the main purpose of the dialog would be to "Scan QR code",
   the text should point that out more, also the opening button.
   Then, the button position would be correct, however,
   it has to read "Cancel" then.

2. _However_, the main purpose of the dialog is to "Show Info",
   "Scan QR code" is only an optional offering and the button a shortcut only.
   And finishing "Show Info" is better done by a simple "OK".
   Then, the "OK" should be rightmost.

This PR implements the second option, the buttons read as follows afterwards:

```
End-to-end encryption cannot...
[More Info]  [Scan QR code] [OK]
```

* fix: load updated display name when chat protection breaks

* Implement feature flag for verified 1:1 chats

* Update res/values/strings.xml

Co-authored-by: Hocuri <hocuri@gmx.de>

* Update src/org/thoughtcrime/securesms/connect/DcHelper.java

* Make another AlertDialog

* Revert "Implement feature flag for verified 1:1 chats"

This reverts commit 2efd17edfc.

* Fix typo

* email server->email provider

---------

Co-authored-by: bjoern <r10s@b44t.com>
Co-authored-by: adbenitez <asieldbenitez@gmail.com>
This commit is contained in:
Hocuri 2023-10-27 17:02:29 +02:00 committed by GitHub
parent 1b3a13a0fa
commit ee966f9006
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 179 additions and 57 deletions

View file

@ -1367,6 +1367,11 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isProtected(JNIEnv *env, jobje
return dc_chat_is_protected(get_dc_chat(env, obj))!=0;
}
JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isProtectionBroken(JNIEnv *env, jobject obj)
{
return dc_chat_is_protection_broken(get_dc_chat(env, obj))!=0;
}
JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isSendingLocations(JNIEnv *env, jobject obj)
{

View file

@ -0,0 +1,11 @@
<vector android:height="18sp" android:viewportHeight="1304.4"
android:viewportWidth="1325.6" android:width="18sp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#ffffff"
android:pathData="M259.8,272.4L1072.6,272.4A0,0 0,0 1,1072.7 272.4L1072.7,1032.2A0,0 0,0 1,1072.6 1032.3L259.8,1032.3A0,0 0,0 1,259.7 1032.2L259.7,272.4A0,0 0,0 1,259.8 272.4z" android:strokeWidth="178.139"/>
<path android:fillColor="#b9b9b9"
android:pathData="M643.6,1303C555.7,1295.1 479.5,1229.9 452.5,1146.8 365.5,1173.3 257.1,1169.4 192.1,1098 135.6,1041.4 126.1,954.9 133.2,879.5 28,840.4 -23.6,710.9 10.9,606.9 31.5,539.3 89.5,491.4 151,461.3 132.5,383.7 109.5,287.3 176.8,208.6 245.9,117.6 351.5,123.1 457,148.5 487.4,54.1 587.2,-8.2 685.1,1.1 768.6,4.6 848,60.7 874.9,140.6c74.6,-12.9 160.9,-20.5 223.1,31.8 50.7,39.9 88.8,99.2 87.8,165.5 0.9,40.1 -11.3,80.4 -17.3,118.5 91,17 150.8,110.4 156,198.8 -1.4,112 -39.9,161.5 -142.2,226.4 -23.1,9.2 -7.6,-23.6 0.1,0.2 15,101.5 -12.8,196.7 -94.3,247 -61.8,38 -138.4,38.2 -207.8,25.9 -31.3,91.2 -125,155 -221.5,149.4 -5.1,0.5 -10.1,-0.7 -15.1,-1.1zM1035.6,509.5 L915.2,385.5 612.9,685.4 411,498.7 289.1,614.8 603.1,931.9Z" android:strokeWidth="1.33333"/>
<path android:fillColor="#00000000"
android:pathData="M64.6,1240 L1265.3,65.5"
android:strokeColor="#ff0c16" android:strokeLineCap="butt"
android:strokeLineJoin="miter" android:strokeWidth="178.139"/>
</vector>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.ConversationUpdateItem
xmlns:android="http://schemas.android.com/apk/res/android"
<org.thoughtcrime.securesms.ConversationUpdateItem xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/conversation_item_background"
android:focusable="true"
android:gravity="center"
@ -13,6 +13,15 @@
android:paddingLeft="28dp"
android:paddingRight="28dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/verified_icon"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_gravity="center_vertical"
tools:src="@drawable/ic_verified"
android:padding="7dp"
android:visibility="gone" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -59,7 +68,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:layout_marginBottom="6dp"
android:tint="?attr/conversation_item_update_text_color"
app:tint="?attr/conversation_item_update_text_color"
android:visibility="gone" />
</LinearLayout>

View file

@ -10,7 +10,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="11dp"
android:paddingTop="11dp"
android:textSize="14sp"
style="@style/Signal.Text.Body"
tools:text="Chat with Alice In Wondeful Wonderland (alice@wonderland.org)?"
app:layout_constraintBottom_toTopOf="@id/message_request_block"
app:layout_constraintEnd_toEndOf="parent"

View file

@ -834,12 +834,13 @@
<string name="ephemeral_timer_weeks_by_you">You set disappearing messages timer to %1$s weeks.</string>
<!-- %1$s will be replaced by the number of weeks (always >1) the timer is set to, %2$s will be replaced by name and address of the contact -->
<string name="ephemeral_timer_weeks_by_other">Disappearing messages timer set to %1$s weeks by %2$s.</string>
<string name="protection_enabled_by_you">You enabled chat protection.</string>
<!-- %1$s will be replaced by name and address of the contact -->
<string name="protection_enabled_by_other">Chat protection enabled by %1$s.</string>
<string name="protection_disabled_by_you">You disabled chat protection.</string>
<!-- %1$s will be replaced by name and address of the contact -->
<string name="protection_disabled_by_other">Chat protection disabled by %1$s.</string>
<!-- this may be shown instead of the chat's input field, with buttons "More Info" and "OK" -->
<string name="chat_protection_broken">%1$s sent a message from another device.</string>
<string name="chat_protection_enabled_tap_to_learn_more">Messages are guaranteed to be end-to-end encrypted from now on. Tap to learn more.</string>
<string name="chat_protection_enabled_explanation">It is now guaranteed that all messages in this chat are end-to-end encrypted.\n\nEnd-to-end encryption keeps messages private between you and your chat partners. Not even your email provider can read them.</string>
<string name="chat_protection_broken_tap_to_learn_more">%1$s sent a message from another device. Tap to learn more.</string>
<string name="chat_protection_broken_explanation">End-to-end encryption cannot be guaranteed anymore, likely because %1$s reinstalled Delta Chat or sent a message from another device.\n\nYou may meet them in person and scan their QR Code again to reestablish guaranteed end-to-end encryption.</string>
<string name="learn_more">Learn More</string>
<string name="devicemsg_self_deleted">You deleted the \"Saved messages\" chat.\n\n To use the \"Saved messages\" feature again, create a new chat with yourself.</string>
<!-- %1$s will be replaced by the amount of storage already used, sth. as '500 MB'. If you want to use a percentage sign, type in two of them, eg. %1$s %% -->
@ -1075,5 +1076,4 @@
<string name="update_1_38_android">1.38 is out:\n\n📎 Removed upper size limit of attachments\n\n🗜 PNG images (screenshots!) are now compressed as well\n\n⌨ For Devs: Better apps for chats with sendToChat() and importFiles() (What is Webxdc? → https://webxdc.org)\n\n… and more at %1$s</string>
<!-- placeholder will be replaced by the URL https://get.delta.chat/#changelogs -->
<string name="update_1_38_ios">1.38 is out:\n\n🖼🔍 Ever searching for an image and could not remember the chat? Then the \"All Media\" button is yours!\n\n🧹 \"Clear Chat\" from the profiles\n\n📎 Removed upper size limit of attachments\n\n🗜 PNG images (screenshots!) are now compressed as well\n\n… and more at %1$s</string>
</resources>

View file

@ -43,6 +43,7 @@ public class DcChat {
public native boolean isDeviceTalk ();
public native boolean canSend ();
public native boolean isProtected ();
public native boolean isProtectionBroken();
public native boolean isSendingLocations();
public native boolean isMuted ();
public native boolean isContactRequest ();
@ -67,6 +68,9 @@ public class DcChat {
return canSend() && !isSelfTalk();
}
public boolean isHalfBlocked() {
return isProtectionBroken() || isContactRequest();
}
// working with raw c-data

View file

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_VERIFIED_ONE_ON_ONE_CHATS;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -138,6 +140,10 @@ public class ApplicationContext extends MultiDexApplication {
}
// /migrating chat backgrounds
for (int accountId : allAccounts) {
dcAccounts.getAccount(accountId).setConfig(CONFIG_VERIFIED_ONE_ON_ONE_CHATS, "1");
}
// set translations before starting I/O to avoid sending untranslated MDNs (issue #2288)
DcHelper.setStockTranslations(this);

View file

@ -17,6 +17,7 @@ import com.b44t.messenger.rpc.Rpc;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.HashSet;
import java.util.Set;
@ -33,6 +34,7 @@ public abstract class BaseConversationItem extends LinearLayout
protected final Context context;
protected final DcContext dcContext;
protected final Rpc rpc;
protected Recipient conversationRecipient;
protected @NonNull Set<DcMsg> batchSelected = new HashSet<>();
@ -48,11 +50,13 @@ public abstract class BaseConversationItem extends LinearLayout
protected void bind(@NonNull DcMsg messageRecord,
@NonNull DcChat dcChat,
@NonNull Set<DcMsg> batchSelected,
boolean pulseHighlight)
boolean pulseHighlight,
@NonNull Recipient conversationRecipient)
{
this.messageRecord = messageRecord;
this.dcChat = dcChat;
this.batchSelected = batchSelected;
this.conversationRecipient = conversationRecipient;
setInteractionState(messageRecord, pulseHighlight);
}
@ -75,7 +79,10 @@ public abstract class BaseConversationItem extends LinearLayout
}
protected boolean shouldInterceptClicks(DcMsg messageRecord) {
return batchSelected.isEmpty() && (messageRecord.isFailed());
return batchSelected.isEmpty()
&& (messageRecord.isFailed()
|| messageRecord.getInfoType() == DcMsg.DC_INFO_PROTECTION_DISABLED
|| messageRecord.getInfoType() == DcMsg.DC_INFO_PROTECTION_ENABLED);
}
protected void onAccessibilityClick() {}
@ -121,6 +128,10 @@ public abstract class BaseConversationItem extends LinearLayout
.setPositiveButton(R.string.ok, null)
.create();
d.show();
} else if (messageRecord.getInfoType() == DcMsg.DC_INFO_PROTECTION_DISABLED) {
DcHelper.showVerificationBrokenDialog(context, conversationRecipient.getName());
} else if (messageRecord.getInfoType() == DcMsg.DC_INFO_PROTECTION_ENABLED) {
DcHelper.showProtectionEnabledDialog(context);
}
}
}

View file

@ -915,8 +915,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void setComposePanelVisibility() {
if (dcChat.canSend()) {
composePanel.setVisibility(View.VISIBLE);
attachmentManager.setHidden(false);
} else {
composePanel.setVisibility(View.GONE);
attachmentManager.setHidden(true);
hideSoftKeyboard();
}
}
@ -1611,7 +1613,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
public void initializeContactRequest() {
if (!dcChat.isContactRequest()) {
if (!dcChat.isHalfBlocked()) {
messageRequestBottomView.setVisibility(View.GONE);
return;
}
@ -1622,6 +1624,24 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
messageRequestBottomView.setVisibility(View.GONE);
composePanel.setVisibility(View.VISIBLE);
});
if (dcChat.isProtectionBroken()) {
messageRequestBottomView.setBlockText(R.string.more_info_desktop);
String name = dcContext.getContact(recipient.getDcContact().getId()).getDisplayName();
messageRequestBottomView.setBlockOnClickListener(v -> DcHelper.showVerificationBrokenDialog(this, name));
messageRequestBottomView.setQuestion(getString(R.string.chat_protection_broken, name));
messageRequestBottomView.setAcceptText(R.string.ok);
} else if (dcChat.getType() == DcChat.DC_CHAT_TYPE_GROUP) {
// We don't support blocking groups yet, so offer to delete it instead
messageRequestBottomView.setBlockText(R.string.delete);
messageRequestBottomView.setBlockOnClickListener(v -> handleDeleteChat());
messageRequestBottomView.setQuestion(null);
} else {
messageRequestBottomView.setBlockText(R.string.block);
messageRequestBottomView.setBlockOnClickListener(v -> {
// avoid showing compose panel on receiving DC_EVENT_CONTACTS_CHANGED for the chat that is no longer a request after blocking
DcHelper.getEventCenter(this).removeObserver(DcContext.DC_EVENT_CONTACTS_CHANGED, this);
@ -1631,12 +1651,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
extras.putInt(ConversationListFragment.RELOAD_LIST, 1);
handleReturnToConversationList(extras);
});
if (dcChat.getType() == DcChat.DC_CHAT_TYPE_GROUP) {
// We don't support blocking groups yet, so offer to delete it instead
messageRequestBottomView.setBlockText(R.string.delete);
messageRequestBottomView.setBlockOnClickListener(v -> handleDeleteChat());
}
messageRequestBottomView.setQuestion(null);
}
}
}

View file

@ -856,7 +856,7 @@ public class ConversationFragment extends MessageSelectorFragment
public void onShowFullClicked(DcMsg messageRecord) {
Intent intent = new Intent(getActivity(), FullMsgActivity.class);
intent.putExtra(FullMsgActivity.MSG_ID_EXTRA, messageRecord.getId());
intent.putExtra(FullMsgActivity.IS_CONTACT_REQUEST, getListAdapter().getChat().isContactRequest());
intent.putExtra(FullMsgActivity.BLOCK_LOADING_REMOTE, getListAdapter().getChat().isHalfBlocked());
startActivity(intent);
getActivity().overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out);
}

View file

@ -108,7 +108,6 @@ public class ConversationItem extends BaseConversationItem
private ViewGroup container;
private Button msgActionButton;
private @NonNull Recipient conversationRecipient;
private @NonNull Stub<ConversationItemThumbnail> mediaThumbnailStub;
private @NonNull Stub<AudioView> audioViewStub;
private @NonNull Stub<DocumentView> documentViewStub;
@ -170,10 +169,9 @@ public class ConversationItem extends BaseConversationItem
@NonNull Recipient recipients,
boolean pulseHighlight)
{
bind(messageRecord, dcChat, batchSelected, pulseHighlight);
bind(messageRecord, dcChat, batchSelected, pulseHighlight, recipients);
this.locale = locale;
this.glideRequests = glideRequests;
this.conversationRecipient = recipients;
this.showSender = dcChat.isMultiUser() || messageRecord.getOverrideSenderName() != null;
if (showSender && !messageRecord.isOutgoing()) {

View file

@ -27,6 +27,7 @@ public class ConversationUpdateItem extends BaseConversationItem
{
private DeliveryStatusView deliveryStatusView;
private AppCompatImageView appIcon;
private AppCompatImageView verifiedIcon;
private int textColor;
public ConversationUpdateItem(Context context) {
@ -46,6 +47,7 @@ public class ConversationUpdateItem extends BaseConversationItem
bodyText = findViewById(R.id.conversation_update_body);
deliveryStatusView = new DeliveryStatusView(findViewById(R.id.delivery_indicator));
appIcon = findViewById(R.id.app_icon);
verifiedIcon = findViewById(R.id.verified_icon);
bodyText.setOnLongClickListener(passthroughClickListener);
@ -62,7 +64,7 @@ public class ConversationUpdateItem extends BaseConversationItem
@NonNull Recipient conversationRecipient,
boolean pulseUpdate)
{
bind(messageRecord, dcChat, batchSelected, pulseUpdate);
bind(messageRecord, dcChat, batchSelected, pulseUpdate, conversationRecipient);
setGenericInfoRecord(messageRecord);
}
@ -87,7 +89,9 @@ public class ConversationUpdateItem extends BaseConversationItem
}
private void setGenericInfoRecord(DcMsg messageRecord) {
if (messageRecord.getInfoType() == DcMsg.DC_INFO_WEBXDC_INFO_MESSAGE) {
int infoType = messageRecord.getInfoType();
if (infoType == DcMsg.DC_INFO_WEBXDC_INFO_MESSAGE) {
DcMsg parentMsg = messageRecord.getParent();
// It is possible that we only received an update without the webxdc itself.
@ -108,6 +112,16 @@ public class ConversationUpdateItem extends BaseConversationItem
appIcon.setVisibility(GONE);
}
if (infoType == DcMsg.DC_INFO_PROTECTION_ENABLED) {
verifiedIcon.setVisibility(VISIBLE);
verifiedIcon.setImageResource(R.drawable.ic_verified);
} else if (infoType == DcMsg.DC_INFO_PROTECTION_DISABLED) {
verifiedIcon.setVisibility(VISIBLE);
verifiedIcon.setImageResource(R.drawable.ic_verified_broken);
} else {
verifiedIcon.setVisibility(GONE);
}
bodyText.setText(messageRecord.getDisplayBody());
bodyText.setVisibility(VISIBLE);

View file

@ -23,12 +23,12 @@ import java.lang.ref.WeakReference;
public class FullMsgActivity extends WebViewActivity
{
public static final String MSG_ID_EXTRA = "msg_id";
public static final String IS_CONTACT_REQUEST = "is_contact_request";
public static final String BLOCK_LOADING_REMOTE = "block_loading_remote";
private int msgId;
private DcContext dcContext;
private Rpc rpc;
private boolean loadRemoteContent = false;
private boolean isContactRequest;
private boolean blockLoadingRemote;
enum LoadRemoteContent {
NEVER,
@ -40,8 +40,8 @@ public class FullMsgActivity extends WebViewActivity
protected void onCreate(Bundle state, boolean ready) {
super.onCreate(state, ready);
isContactRequest = getIntent().getBooleanExtra(IS_CONTACT_REQUEST, false);
loadRemoteContent = !isContactRequest && Prefs.getAlwaysLoadRemoteContent(this);
blockLoadingRemote = getIntent().getBooleanExtra(BLOCK_LOADING_REMOTE, false);
loadRemoteContent = !blockLoadingRemote && Prefs.getAlwaysLoadRemoteContent(this);
webView.getSettings().setBlockNetworkLoads(!loadRemoteContent);
// setBuiltInZoomControls() adds pinch-to-zoom as well as two on-screen zoom control buttons.
@ -128,7 +128,7 @@ public class FullMsgActivity extends WebViewActivity
String alwaysCheckmark = "";
String onceCheckmark = "";
String neverCheckmark = "";
if (!isContactRequest && Prefs.getAlwaysLoadRemoteContent(this)) {
if (!blockLoadingRemote && Prefs.getAlwaysLoadRemoteContent(this)) {
alwaysCheckmark = checkmark;
} else if (loadRemoteContent) {
onceCheckmark = checkmark;
@ -136,10 +136,10 @@ public class FullMsgActivity extends WebViewActivity
neverCheckmark = checkmark;
}
if (!isContactRequest) {
if (!blockLoadingRemote) {
builder.setNeutralButton(alwaysCheckmark + getString(R.string.always), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.ALWAYS));
}
builder.setNegativeButton(neverCheckmark + getString(isContactRequest? R.string.no : R.string.never), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.NEVER));
builder.setNegativeButton(neverCheckmark + getString(blockLoadingRemote ? R.string.no : R.string.never), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.NEVER));
builder.setPositiveButton(onceCheckmark + getString(R.string.once), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.ONCE));
builder.show();
@ -152,13 +152,13 @@ public class FullMsgActivity extends WebViewActivity
switch (loadRemoteContent) {
case NEVER:
this.loadRemoteContent = false;
if (!isContactRequest) {
if (!blockLoadingRemote) {
Prefs.setBooleanPreference(this, Prefs.ALWAYS_LOAD_REMOTE_CONTENT, false);
}
break;
case ONCE:
this.loadRemoteContent = true;
if (!isContactRequest) {
if (!blockLoadingRemote) {
Prefs.setBooleanPreference(this, Prefs.ALWAYS_LOAD_REMOTE_CONTENT, false);
}
break;

View file

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.connect;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_VERIFIED_ONE_ON_ONE_CHATS;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@ -31,6 +33,7 @@ public class AccountManager {
ApplicationContext appContext = (ApplicationContext)context.getApplicationContext();
appContext.dcContext = appContext.dcAccounts.getSelectedAccount();
DcHelper.setStockTranslations(context);
DcHelper.getContext(context).setConfig(CONFIG_VERIFIED_ONE_ON_ONE_CHATS, "1");
DirectShareUtil.resetAllShortcuts(appContext);
}

View file

@ -14,6 +14,7 @@ import android.webkit.MimeTypeMap;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.FileProvider;
import com.b44t.messenger.DcAccounts;
@ -30,8 +31,10 @@ import org.thoughtcrime.securesms.ShareActivity;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.notifications.NotificationCenter;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.qr.QrActivity;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.IntentUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import java.io.File;
@ -74,6 +77,7 @@ public class DcHelper {
public static final String CONFIG_SOCKS5_PORT = "socks5_port";
public static final String CONFIG_SOCKS5_USER = "socks5_user";
public static final String CONFIG_SOCKS5_PASSWORD = "socks5_password";
public static final String CONFIG_VERIFIED_ONE_ON_ONE_CHATS = "verified_one_on_one_chats";
public static DcContext getContext(@NonNull Context context) {
return ApplicationContext.getInstance(context).dcContext;
@ -244,6 +248,11 @@ public class DcHelper {
dcContext.setStockTranslation(123, context.getString(R.string.aeap_explanation));
dcContext.setStockTranslation(162, context.getString(R.string.multidevice_qr_subtitle));
dcContext.setStockTranslation(163, context.getString(R.string.multidevice_transfer_done_devicemsg));
// The next two strings should only be set if the UI actually shows more info when the user clicks on the
// DC_INFO_PROTECTION_{EN|DIS}ABLED info message
dcContext.setStockTranslation(170, context.getString(R.string.chat_protection_enabled_tap_to_learn_more));
dcContext.setStockTranslation(171, context.getString(R.string.chat_protection_broken_tap_to_learn_more));
}
public static File getImexDir() {
@ -438,4 +447,26 @@ public class DcHelper {
return context.getString(R.string.connectivity_not_connected);
}
}
public static void showVerificationBrokenDialog(Context context, String name) {
new AlertDialog.Builder(context)
.setMessage(context.getString(R.string.chat_protection_broken_explanation, name))
.setNeutralButton(R.string.learn_more, (d, w) -> IntentUtils.showBrowserIntent(context, "https://staging.delta.chat/684/en/help#verificationbroken"))
.setNegativeButton(R.string.qrscan_title, (d, w) -> context.startActivity(new Intent(context, QrActivity.class)))
.setPositiveButton(R.string.ok, null)
.setCancelable(true)
.show();
}
public static void showProtectionEnabledDialog(Context context) {
new AlertDialog.Builder(context)
.setMessage(context.getString(R.string.chat_protection_enabled_explanation))
.setNeutralButton(R.string.learn_more, (d, w) -> IntentUtils.showBrowserIntent(context, "https://staging.delta.chat/733/en/help#verifiedchats"))
.setNegativeButton(R.string.qrscan_title, (d, w) -> context.startActivity(new Intent(context, QrActivity.class)))
.setPositiveButton(R.string.ok, null)
.setCancelable(true)
.show();
// One day, it would be nice to point the user to the local help:
//context.startActivity(new Intent(context, LocalHelpActivity.class));
}
}

View file

@ -43,6 +43,10 @@ public class MessageRequestsBottomView extends ConstraintLayout {
accept.setOnClickListener(acceptOnClickListener);
}
public void setAcceptText(int text) {
accept.setText(text);
}
public void setBlockOnClickListener(OnClickListener deleteOnClickListener) {
block.setOnClickListener(deleteOnClickListener);
}

View file

@ -99,6 +99,8 @@ public class AttachmentManager {
private @NonNull Optional<Slide> slide = Optional.absent();
private @Nullable Uri imageCaptureUri;
private @Nullable Uri videoCaptureUri;
private boolean attachmentPresent;
private boolean hidden;
public AttachmentManager(@NonNull Activity activity, @NonNull AttachmentListener listener) {
this.context = activity;
@ -136,7 +138,7 @@ public class AttachmentManager {
@Override
public void onSuccess(Boolean result) {
thumbnail.clear(glideRequests);
attachmentViewStub.get().setVisibility(View.GONE);
setAttachmentPresent(false);
attachmentListener.onAttachmentChanged();
}
@ -146,7 +148,7 @@ public class AttachmentManager {
});
} else {
thumbnail.clear(glideRequests);
attachmentViewStub.get().setVisibility(View.GONE);
setAttachmentPresent(false);
attachmentListener.onAttachmentChanged();
}
@ -245,7 +247,7 @@ public class AttachmentManager {
@Override
protected void onPreExecute() {
thumbnail.clear(glideRequests);
attachmentViewStub.get().setVisibility(View.VISIBLE);
setAttachmentPresent(true);
}
@Override
@ -271,18 +273,18 @@ public class AttachmentManager {
@Override
protected void onPostExecute(@Nullable final Slide slide) {
if (slide == null) {
attachmentViewStub.get().setVisibility(View.GONE);
setAttachmentPresent(false);
result.set(false);
} else if (slide.getFileSize()>1*1024*1024*1024) {
// this is only a rough check, videos and images may be recoded
// and the core checks more carefully later.
attachmentViewStub.get().setVisibility(View.GONE);
setAttachmentPresent(false);
Log.w(TAG, "File too large.");
Toast.makeText(slide.context, "File too large.", Toast.LENGTH_LONG).show();
result.set(false);
} else {
setSlide(slide);
attachmentViewStub.get().setVisibility(View.VISIBLE);
setAttachmentPresent(true);
if (slide.hasAudio()) {
class SetDurationListener implements AudioSlidePlayer.Listener {
@ -412,7 +414,7 @@ public class AttachmentManager {
}
public boolean isAttachmentPresent() {
return attachmentViewStub.resolved() && attachmentViewStub.get().getVisibility() == View.VISIBLE;
return attachmentPresent;
}
public @NonNull SlideDeck buildSlideDeck() {
@ -695,17 +697,26 @@ public class AttachmentManager {
}
}
public int getVisibility() {
try {
return attachmentViewStub.get().getVisibility();
} catch(Exception e) {
return View.GONE;
}
public void setHidden(boolean hidden) {
this.hidden = hidden;
updateVisibility();
}
public void setVisibility(int vis) {
try {
private void setAttachmentPresent(boolean isPresent) {
this.attachmentPresent = isPresent;
updateVisibility();
}
private void updateVisibility() {
int vis;
if (attachmentPresent && !hidden) {
vis = View.VISIBLE;
} else {
vis = View.GONE;
}
if (vis == View.GONE && !attachmentViewStub.resolved()) {
return;
}
attachmentViewStub.get().setVisibility(vis);
} catch(Exception e) {}
}
}