mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-22 22:46:21 +00:00
Support for sealed sender - Part 2
This commit is contained in:
@@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.GcmRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
||||
import org.thoughtcrime.securesms.logging.AndroidLogger;
|
||||
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
@@ -109,6 +110,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
initializeCircumvention();
|
||||
initializeWebRtc();
|
||||
initializePendingMessages();
|
||||
initializeUnidentifiedDeliveryAbilityRefresh();
|
||||
NotificationChannels.create(this);
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||
}
|
||||
@@ -281,4 +283,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
TextSecurePreferences.setNeedsMessagePull(this, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeUnidentifiedDeliveryAbilityRefresh() {
|
||||
if (TextSecurePreferences.isMultiDevice(this) && !TextSecurePreferences.isUnidentifiedDeliveryEnabled(this)) {
|
||||
jobManager.add(new RefreshUnidentifiedDeliveryAbilityJob(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.FileUtils;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
@@ -95,6 +94,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
|
||||
public static final int IMAGE_CACHE_CLEANUP = 406;
|
||||
public static final int WORKMANAGER_MIGRATION = 408;
|
||||
public static final int COLOR_MIGRATION = 412;
|
||||
public static final int UNIDENTIFIED_DELIVERY = 422;
|
||||
|
||||
private static final SortedSet<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{
|
||||
add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION);
|
||||
@@ -120,6 +120,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
|
||||
add(IMAGE_CACHE_CLEANUP);
|
||||
add(WORKMANAGER_MIGRATION);
|
||||
add(COLOR_MIGRATION);
|
||||
add(UNIDENTIFIED_DELIVERY);
|
||||
}};
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
@@ -358,6 +359,18 @@ public class DatabaseUpgradeActivity extends BaseActivity {
|
||||
Log.i(TAG, "Color migration took " + (System.currentTimeMillis() - startTime) + " ms");
|
||||
}
|
||||
|
||||
if (params[0] < UNIDENTIFIED_DELIVERY) {
|
||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||
Log.i(TAG, "MultiDevice: Disabling UD (will be re-enabled if possible after pending refresh).");
|
||||
TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, false);
|
||||
}
|
||||
|
||||
Log.i(TAG, "Scheduling UD attributes refresh.");
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new RefreshAttributesJob(context));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.os.Vibrator;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import android.transition.TransitionInflater;
|
||||
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
@@ -179,6 +180,7 @@ public class DeviceActivity extends PassphraseRequiredActionBarActivity
|
||||
Optional<byte[]> profileKey = Optional.of(ProfileKeyUtil.getProfileKey(getContext()));
|
||||
|
||||
TextSecurePreferences.setMultiDevice(DeviceActivity.this, true);
|
||||
TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, false);
|
||||
accountManager.addDevice(ephemeralId, publicKey, identityKeyPair, profileKey, verificationCode);
|
||||
|
||||
return SUCCESS;
|
||||
|
||||
@@ -9,6 +9,8 @@ import android.support.v4.app.ListFragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -172,6 +174,10 @@ public class DeviceListFragment extends ListFragment
|
||||
protected Void doInBackground(Void... params) {
|
||||
try {
|
||||
accountManager.removeDevice(deviceId);
|
||||
|
||||
ApplicationContext.getInstance(getContext())
|
||||
.getJobManager()
|
||||
.add(new RefreshUnidentifiedDeliveryAbilityJob(getContext()));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
Toast.makeText(getActivity(), R.string.DeviceListActivity_network_failed, Toast.LENGTH_LONG).show();
|
||||
|
||||
@@ -52,6 +52,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
@@ -87,6 +88,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
private View metadataContainer;
|
||||
private View expiresContainer;
|
||||
private TextView errorText;
|
||||
private View resendButton;
|
||||
private TextView sentDate;
|
||||
private TextView receivedDate;
|
||||
private TextView expiresInText;
|
||||
@@ -176,6 +178,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
recipientsList = findViewById(R.id.recipients_list);
|
||||
metadataContainer = header.findViewById(R.id.metadata_container);
|
||||
errorText = header.findViewById(R.id.error_text);
|
||||
resendButton = header.findViewById(R.id.resend_button);
|
||||
sentDate = header.findViewById(R.id.sent_time);
|
||||
receivedContainer = header.findViewById(R.id.received_container);
|
||||
receivedDate = header.findViewById(R.id.received_time);
|
||||
@@ -362,7 +365,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
List<RecipientDeliveryStatus> recipients = new LinkedList<>();
|
||||
|
||||
if (!messageRecord.getRecipient().isGroupRecipient()) {
|
||||
recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), -1));
|
||||
recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), messageRecord.isUnidentified(), -1));
|
||||
} else {
|
||||
List<GroupReceiptInfo> receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId());
|
||||
|
||||
@@ -370,12 +373,13 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().getAddress().toGroupString(), false);
|
||||
|
||||
for (Recipient recipient : group) {
|
||||
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, -1));
|
||||
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1));
|
||||
}
|
||||
} else {
|
||||
for (GroupReceiptInfo info : receiptInfoList) {
|
||||
recipients.add(new RecipientDeliveryStatus(Recipient.from(context, info.getAddress(), true),
|
||||
getStatusFor(info.getStatus(), messageRecord.isPending()),
|
||||
getStatusFor(info.getStatus(), messageRecord.isPending(), messageRecord.isFailed()),
|
||||
info.isUnidentified(),
|
||||
info.getTimestamp()));
|
||||
}
|
||||
}
|
||||
@@ -392,16 +396,28 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
inflateMessageViewIfAbsent(messageRecord);
|
||||
|
||||
updateRecipients(messageRecord, messageRecord.getRecipient(), recipients);
|
||||
if (messageRecord.isFailed()) {
|
||||
|
||||
boolean isGroupNetworkFailure = messageRecord.isFailed() && !messageRecord.getNetworkFailures().isEmpty();
|
||||
boolean isIndividualNetworkFailure = messageRecord.isFailed() && !isPushGroup && messageRecord.getIdentityKeyMismatches().isEmpty();
|
||||
|
||||
if (isGroupNetworkFailure || isIndividualNetworkFailure) {
|
||||
errorText.setVisibility(View.VISIBLE);
|
||||
resendButton.setVisibility(View.VISIBLE);
|
||||
resendButton.setOnClickListener(this::onResendClicked);
|
||||
metadataContainer.setVisibility(View.GONE);
|
||||
} else if (messageRecord.isFailed()) {
|
||||
errorText.setVisibility(View.VISIBLE);
|
||||
resendButton.setVisibility(View.GONE);
|
||||
resendButton.setOnClickListener(null);
|
||||
metadataContainer.setVisibility(View.GONE);
|
||||
} else {
|
||||
updateTransport(messageRecord);
|
||||
updateTime(messageRecord);
|
||||
updateExpirationTime(messageRecord);
|
||||
errorText.setVisibility(View.GONE);
|
||||
resendButton.setVisibility(View.GONE);
|
||||
resendButton.setOnClickListener(null);
|
||||
metadataContainer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
@@ -413,14 +429,19 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
else return RecipientDeliveryStatus.Status.PENDING;
|
||||
}
|
||||
|
||||
private RecipientDeliveryStatus.Status getStatusFor(int groupStatus, boolean pending) {
|
||||
private RecipientDeliveryStatus.Status getStatusFor(int groupStatus, boolean pending, boolean failed) {
|
||||
if (groupStatus == GroupReceiptDatabase.STATUS_READ) return RecipientDeliveryStatus.Status.READ;
|
||||
else if (groupStatus == GroupReceiptDatabase.STATUS_DELIVERED) return RecipientDeliveryStatus.Status.DELIVERED;
|
||||
else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED && failed) return RecipientDeliveryStatus.Status.UNKNOWN;
|
||||
else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED && !pending) return RecipientDeliveryStatus.Status.SENT;
|
||||
else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED) return RecipientDeliveryStatus.Status.PENDING;
|
||||
else if (groupStatus == GroupReceiptDatabase.STATUS_UNKNOWN) return RecipientDeliveryStatus.Status.UNKNOWN;
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
private void onResendClicked(View v) {
|
||||
MessageSender.resend(MessageDetailsActivity.this, messageRecord);
|
||||
resendButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,11 +81,13 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
|
||||
|
||||
private final Recipient recipient;
|
||||
private final Status deliveryStatus;
|
||||
private final boolean isUnidentified;
|
||||
private final long timestamp;
|
||||
|
||||
RecipientDeliveryStatus(Recipient recipient, Status deliveryStatus, long timestamp) {
|
||||
RecipientDeliveryStatus(Recipient recipient, Status deliveryStatus, boolean isUnidentified, long timestamp) {
|
||||
this.recipient = recipient;
|
||||
this.deliveryStatus = deliveryStatus;
|
||||
this.isUnidentified = isUnidentified;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
@@ -93,6 +95,10 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
|
||||
return deliveryStatus;
|
||||
}
|
||||
|
||||
boolean isUnidentified() {
|
||||
return isUnidentified;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -30,15 +29,13 @@ import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDelive
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.components.DeliveryStatusView;
|
||||
import org.thoughtcrime.securesms.components.FromTextView;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
/**
|
||||
@@ -58,8 +55,8 @@ public class MessageRecipientListItem extends RelativeLayout
|
||||
private TextView errorDescription;
|
||||
private TextView actionDescription;
|
||||
private Button conflictButton;
|
||||
private Button resendButton;
|
||||
private AvatarImageView contactPhotoImage;
|
||||
private ImageView unidentifiedDeliveryIcon;
|
||||
private DeliveryStatusView deliveryStatusView;
|
||||
|
||||
public MessageRecipientListItem(Context context) {
|
||||
@@ -73,13 +70,13 @@ public class MessageRecipientListItem extends RelativeLayout
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
this.fromView = findViewById(R.id.from);
|
||||
this.errorDescription = findViewById(R.id.error_description);
|
||||
this.actionDescription = findViewById(R.id.action_description);
|
||||
this.contactPhotoImage = findViewById(R.id.contact_photo_image);
|
||||
this.conflictButton = findViewById(R.id.conflict_button);
|
||||
this.resendButton = findViewById(R.id.resend_button);
|
||||
this.deliveryStatusView = findViewById(R.id.delivery_status);
|
||||
this.fromView = findViewById(R.id.from);
|
||||
this.errorDescription = findViewById(R.id.error_description);
|
||||
this.actionDescription = findViewById(R.id.action_description);
|
||||
this.contactPhotoImage = findViewById(R.id.contact_photo_image);
|
||||
this.conflictButton = findViewById(R.id.conflict_button);
|
||||
this.unidentifiedDeliveryIcon = findViewById(R.id.ud_indicator);
|
||||
this.deliveryStatusView = findViewById(R.id.delivery_status);
|
||||
}
|
||||
|
||||
public void set(final GlideRequests glideRequests,
|
||||
@@ -94,6 +91,7 @@ public class MessageRecipientListItem extends RelativeLayout
|
||||
fromView.setText(member.getRecipient());
|
||||
contactPhotoImage.setAvatar(glideRequests, member.getRecipient(), false);
|
||||
setIssueIndicators(record, isPushGroup);
|
||||
unidentifiedDeliveryIcon.setVisibility(TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()) && member.isUnidentified() ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
private void setIssueIndicators(final MessageRecord record,
|
||||
@@ -105,25 +103,13 @@ public class MessageRecipientListItem extends RelativeLayout
|
||||
String errorText = "";
|
||||
|
||||
if (keyMismatch != null) {
|
||||
resendButton.setVisibility(View.GONE);
|
||||
conflictButton.setVisibility(View.VISIBLE);
|
||||
|
||||
errorText = getContext().getString(R.string.MessageDetailsRecipient_new_safety_number);
|
||||
conflictButton.setOnClickListener(v -> new ConfirmIdentityDialog(getContext(), record, keyMismatch).show());
|
||||
} else if (networkFailure != null || (!isPushGroup && record.isFailed())) {
|
||||
resendButton.setVisibility(View.VISIBLE);
|
||||
resendButton.setEnabled(true);
|
||||
resendButton.requestFocus();
|
||||
} else if ((networkFailure != null && !record.isPending()) || (!isPushGroup && record.isFailed())) {
|
||||
conflictButton.setVisibility(View.GONE);
|
||||
|
||||
errorText = getContext().getString(R.string.MessageDetailsRecipient_failed_to_send);
|
||||
resendButton.setOnClickListener(v -> {
|
||||
resendButton.setVisibility(View.GONE);
|
||||
errorDescription.setVisibility(View.GONE);
|
||||
actionDescription.setVisibility(View.VISIBLE);
|
||||
actionDescription.setText(R.string.message_recipients_list_item__resending);
|
||||
new ResendAsyncTask(record, networkFailure).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
});
|
||||
} else {
|
||||
if (record.isOutgoing()) {
|
||||
if (member.getDeliveryStatus() == RecipientDeliveryStatus.Status.PENDING || member.getDeliveryStatus() == RecipientDeliveryStatus.Status.UNKNOWN) {
|
||||
@@ -142,7 +128,6 @@ public class MessageRecipientListItem extends RelativeLayout
|
||||
deliveryStatusView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
resendButton.setVisibility(View.GONE);
|
||||
conflictButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@@ -183,31 +168,4 @@ public class MessageRecipientListItem extends RelativeLayout
|
||||
contactPhotoImage.setAvatar(glideRequests, recipient, false);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private class ResendAsyncTask extends AsyncTask<Void,Void,Void> {
|
||||
private final Context context;
|
||||
private final MessageRecord record;
|
||||
private final NetworkFailure failure;
|
||||
|
||||
ResendAsyncTask(MessageRecord record, NetworkFailure failure) {
|
||||
this.context = getContext().getApplicationContext();
|
||||
this.record = record;
|
||||
this.failure = failure;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
|
||||
mmsDatabase.removeFailure(record.getId(), failure);
|
||||
|
||||
if (record.getRecipient().isPushGroupRecipient()) {
|
||||
MessageSender.resendGroupMessage(context, record, failure.getAddress());
|
||||
} else {
|
||||
MessageSender.resend(context, record);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,9 +7,8 @@ import android.support.v7.widget.SwitchCompat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceReadReceiptUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
@@ -39,7 +38,9 @@ public class ReadReceiptsIntroFragment extends Fragment {
|
||||
TextSecurePreferences.setReadReceiptsEnabled(getContext(), isChecked);
|
||||
ApplicationContext.getInstance(getContext())
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceReadReceiptUpdateJob(getContext(), isChecked));
|
||||
.add(new MultiDeviceConfigurationUpdateJob(getContext(),
|
||||
isChecked,
|
||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext())));
|
||||
});
|
||||
|
||||
return v;
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.telephony.PhoneNumberUtils;
|
||||
|
||||
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.MenuItem;
|
||||
@@ -740,6 +741,12 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
}
|
||||
}
|
||||
|
||||
if (blocked && (recipient.resolve().isSystemContact() || recipient.resolve().isProfileSharing())) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new RotateProfileKeyJob(context));
|
||||
}
|
||||
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceBlockedUpdateJob(context));
|
||||
|
||||
@@ -31,4 +31,8 @@ public class ProfileKeyUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized @NonNull byte[] rotateProfileKey(@NonNull Context context) {
|
||||
TextSecurePreferences.setProfileKey(context, null);
|
||||
return getProfileKey(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.util.Log;
|
||||
import org.signal.libsignal.metadata.certificate.CertificateValidator;
|
||||
import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
@@ -40,6 +41,11 @@ public class UnidentifiedAccessUtil {
|
||||
public static Optional<UnidentifiedAccessPair> getAccessFor(@NonNull Context context,
|
||||
@NonNull Recipient recipient)
|
||||
{
|
||||
if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
|
||||
Log.i(TAG, "Unidentified delivery is disabled. [other]");
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
|
||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
||||
@@ -49,9 +55,9 @@ public class UnidentifiedAccessUtil {
|
||||
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
||||
}
|
||||
|
||||
Log.w(TAG, "Their access key: " + (theirUnidentifiedAccessKey == null));
|
||||
Log.w(TAG, "Our access key: " + (ourUnidentifiedAccessKey == null));
|
||||
Log.w(TAG, "Our certificatE: " + (ourUnidentifiedAccessCertificate == null));
|
||||
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null));
|
||||
Log.i(TAG, "Our access key present? " + (ourUnidentifiedAccessKey != null));
|
||||
Log.i(TAG, "Our certificate present? " + (ourUnidentifiedAccessCertificate != null));
|
||||
|
||||
if (theirUnidentifiedAccessKey != null &&
|
||||
ourUnidentifiedAccessKey != null &&
|
||||
@@ -71,6 +77,11 @@ public class UnidentifiedAccessUtil {
|
||||
}
|
||||
|
||||
public static Optional<UnidentifiedAccessPair> getAccessForSync(@NonNull Context context) {
|
||||
if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
|
||||
Log.i(TAG, "Unidentified delivery is disabled. [self]");
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
||||
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
|
||||
|
||||
@@ -34,6 +34,7 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
|
||||
public abstract void markExpireStarted(long messageId, long startTime);
|
||||
|
||||
public abstract void markAsSent(long messageId, boolean secure);
|
||||
public abstract void markUnidentified(long messageId, boolean unidentified);
|
||||
|
||||
public void setMismatchedIdentity(long messageId, final Address address, final IdentityKey identityKey) {
|
||||
List<IdentityKeyMismatch> items = new ArrayList<IdentityKeyMismatch>() {{
|
||||
|
||||
@@ -343,11 +343,11 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
notifyConversationListeners(threadId);
|
||||
}
|
||||
|
||||
// public void markAsSending(long messageId) {
|
||||
// long threadId = getThreadIdForMessage(messageId);
|
||||
// updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE, Optional.of(threadId));
|
||||
// notifyConversationListeners(threadId);
|
||||
// }
|
||||
public void markAsSending(long messageId) {
|
||||
long threadId = getThreadIdForMessage(messageId);
|
||||
updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE, Optional.of(threadId));
|
||||
notifyConversationListeners(threadId);
|
||||
}
|
||||
|
||||
public void markAsSentFailed(long messageId) {
|
||||
long threadId = getThreadIdForMessage(messageId);
|
||||
@@ -403,6 +403,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
notifyConversationListeners(threadId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markUnidentified(long messageId, boolean unidentified) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0);
|
||||
|
||||
@@ -270,8 +270,7 @@ public class MmsSmsDatabase extends Database {
|
||||
MmsDatabase.QUOTE_BODY,
|
||||
MmsDatabase.QUOTE_MISSING,
|
||||
MmsDatabase.QUOTE_ATTACHMENT,
|
||||
MmsDatabase.SHARED_CONTACTS,
|
||||
MmsDatabase.UNIDENTIFIED};
|
||||
MmsDatabase.SHARED_CONTACTS};
|
||||
|
||||
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
|
||||
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
|
||||
|
||||
@@ -239,10 +239,15 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENT_TYPE | (isSecure ? Types.PUSH_MESSAGE_BIT | Types.SECURE_MESSAGE_BIT : 0));
|
||||
}
|
||||
|
||||
public void markAsSending(long id) {
|
||||
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE);
|
||||
}
|
||||
|
||||
public void markAsMissedCall(long id) {
|
||||
updateTypeBitmask(id, Types.TOTAL_MASK, Types.MISSED_CALL_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markUnidentified(long id, boolean unidentified) {
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0);
|
||||
@@ -695,7 +700,9 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
|
||||
public Cursor getMessageCursor(long messageId) {
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
return db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[] {messageId + ""}, null, null, null);
|
||||
Cursor cursor = db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[] {messageId + ""}, null, null, null);
|
||||
setNotifyConverationListeners(cursor, getThreadIdForMessage(messageId));
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public boolean deleteMessage(long messageId) {
|
||||
|
||||
@@ -289,6 +289,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN unidentified_access_mode INTEGER DEFAULT 0");
|
||||
db.execSQL("ALTER TABLE push ADD COLUMN server_timestamp INTEGER DEFAULT 0");
|
||||
db.execSQL("ALTER TABLE push ADD COLUMN server_guid TEXT DEFAULT NULL");
|
||||
db.execSQL("ALTER TABLE group_receipts ADD COLUMN unidentified INTEGER DEFAULT 0");
|
||||
db.execSQL("ALTER TABLE mms ADD COLUMN unidentified INTEGER DEFAULT 0");
|
||||
db.execSQL("ALTER TABLE sms ADD COLUMN unidentified INTEGER DEFAULT 0");
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
|
||||
@@ -3,6 +3,9 @@ package org.thoughtcrime.securesms.dependencies;
|
||||
import android.content.Context;
|
||||
|
||||
import org.thoughtcrime.securesms.gcm.GcmBroadcastReceiver;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
@@ -87,7 +90,10 @@ import dagger.Provides;
|
||||
AppProtectionPreferenceFragment.class,
|
||||
GcmBroadcastReceiver.class,
|
||||
RotateCertificateJob.class,
|
||||
SendDeliveryReceiptJob.class})
|
||||
SendDeliveryReceiptJob.class,
|
||||
RotateProfileKeyJob.class,
|
||||
MultiDeviceConfigurationUpdateJob.class,
|
||||
RefreshUnidentifiedDeliveryAbilityJob.class})
|
||||
public class SignalCommunicationModule {
|
||||
|
||||
private static final String TAG = SignalCommunicationModule.class.getSimpleName();
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
@@ -13,6 +14,8 @@ import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage;
|
||||
@@ -61,6 +64,11 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
|
||||
public void onRun(MasterSecret masterSecret)
|
||||
throws IOException, UntrustedIdentityException
|
||||
{
|
||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
||||
Log.i(TAG, "Not multi device, aborting...");
|
||||
return;
|
||||
}
|
||||
|
||||
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
|
||||
|
||||
try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.work.Data;
|
||||
|
||||
public class MultiDeviceConfigurationUpdateJob extends ContextJob implements InjectableType {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String TAG = MultiDeviceConfigurationUpdateJob.class.getSimpleName();
|
||||
|
||||
private static final String KEY_READ_RECEIPTS_ENABLED = "read_receipts_enabled";
|
||||
private static final String KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED = "unidentified_delivery_indicators_enabled";
|
||||
|
||||
@Inject transient SignalServiceMessageSender messageSender;
|
||||
|
||||
private boolean readReceiptsEnabled;
|
||||
private boolean unidentifiedDeliveryIndicatorsEnabled;
|
||||
|
||||
public MultiDeviceConfigurationUpdateJob() {
|
||||
super(null, null);
|
||||
}
|
||||
|
||||
public MultiDeviceConfigurationUpdateJob(Context context, boolean readReceiptsEnabled, boolean unidentifiedDeliveryIndicatorsEnabled) {
|
||||
super(context, JobParameters.newBuilder()
|
||||
.withGroupId("__MULTI_DEVICE_CONFIGURATION_UPDATE_JOB__")
|
||||
.withNetworkRequirement()
|
||||
.create());
|
||||
|
||||
this.readReceiptsEnabled = readReceiptsEnabled;
|
||||
this.unidentifiedDeliveryIndicatorsEnabled = unidentifiedDeliveryIndicatorsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize(@NonNull SafeData data) {
|
||||
readReceiptsEnabled = data.getBoolean(KEY_READ_RECEIPTS_ENABLED);
|
||||
unidentifiedDeliveryIndicatorsEnabled = data.getBoolean(KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
|
||||
return dataBuilder.putBoolean(KEY_READ_RECEIPTS_ENABLED, readReceiptsEnabled)
|
||||
.putBoolean(KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED, unidentifiedDeliveryIndicatorsEnabled)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws IOException, UntrustedIdentityException {
|
||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
||||
Log.i(TAG, "Not multi device, aborting...");
|
||||
return;
|
||||
}
|
||||
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(readReceiptsEnabled), Optional.of(unidentifiedDeliveryIndicatorsEnabled))),
|
||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(Exception e) {
|
||||
return e instanceof PushNetworkException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {
|
||||
Log.w(TAG, "**** Failed to synchronize read receipts state!");
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
|
||||
throws IOException, UntrustedIdentityException, NetworkException
|
||||
{
|
||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
||||
Log.w(TAG, "Not multi device, aborting...");
|
||||
Log.i(TAG, "Not multi device, aborting...");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
@@ -67,6 +68,11 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
|
||||
|
||||
@Override
|
||||
public void onRun(MasterSecret masterSecret) throws Exception {
|
||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
||||
Log.i(TAG, "Not multi device, aborting...");
|
||||
return;
|
||||
}
|
||||
|
||||
File contactDataFile = createTempFile("multidevice-contact-update");
|
||||
GroupDatabase.Reader reader = null;
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ public class MultiDeviceProfileKeyUpdateJob extends MasterSecretJob implements I
|
||||
@Override
|
||||
public void onRun(MasterSecret masterSecret) throws IOException, UntrustedIdentityException {
|
||||
if (!TextSecurePreferences.isMultiDevice(getContext())) {
|
||||
Log.w(TAG, "Not multi device...");
|
||||
Log.i(TAG, "Not multi device...");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
@@ -22,6 +23,10 @@ import javax.inject.Inject;
|
||||
|
||||
import androidx.work.Data;
|
||||
|
||||
/**
|
||||
* Use {@link MultiDeviceConfigurationUpdateJob}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class MultiDeviceReadReceiptUpdateJob extends ContextJob implements InjectableType {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -59,7 +64,12 @@ public class MultiDeviceReadReceiptUpdateJob extends ContextJob implements Injec
|
||||
|
||||
@Override
|
||||
public void onRun() throws IOException, UntrustedIdentityException {
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(enabled))),
|
||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
||||
Log.i(TAG, "Not multi device, aborting...");
|
||||
return;
|
||||
}
|
||||
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(enabled), Optional.absent())),
|
||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
|
||||
@@ -91,7 +92,7 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
|
||||
@Override
|
||||
public void onRun(MasterSecret masterSecret) throws IOException, UntrustedIdentityException {
|
||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
||||
Log.w(TAG, "Not multi device...");
|
||||
Log.i(TAG, "Not multi device...");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,8 +102,7 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
|
||||
readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp));
|
||||
}
|
||||
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages),
|
||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages), UnidentifiedAccessUtil.getAccessForSync(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -91,7 +91,7 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab
|
||||
public void onRun() throws IOException, UntrustedIdentityException {
|
||||
try {
|
||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
||||
Log.w(TAG, "Not multi device...");
|
||||
Log.i(TAG, "Not multi device...");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.signal.libsignal.metadata.ProtocolInvalidVersionException;
|
||||
import org.signal.libsignal.metadata.ProtocolLegacyMessageException;
|
||||
import org.signal.libsignal.metadata.ProtocolNoSessionException;
|
||||
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
|
||||
import org.signal.libsignal.metadata.SelfSendException;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.ConversationListActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
@@ -36,6 +37,7 @@ import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||
@@ -242,6 +244,8 @@ public class PushDecryptJob extends ContextJob {
|
||||
handleNeedsDeliveryReceipt(content, message);
|
||||
}
|
||||
} else if (content.getSyncMessage().isPresent()) {
|
||||
TextSecurePreferences.setMultiDevice(context, true);
|
||||
|
||||
SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
|
||||
|
||||
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get());
|
||||
@@ -273,7 +277,11 @@ public class PushDecryptJob extends ContextJob {
|
||||
} catch (ProtocolInvalidVersionException e) {
|
||||
Log.w(TAG, e);
|
||||
handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||
} catch (ProtocolInvalidMessageException | ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
|
||||
} catch (ProtocolInvalidMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||
}
|
||||
catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
|
||||
Log.w(TAG, e);
|
||||
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||
} catch (StorageFailedException e) {
|
||||
@@ -290,6 +298,8 @@ public class PushDecryptJob extends ContextJob {
|
||||
handleDuplicateMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||
} catch (InvalidMetadataVersionException | InvalidMetadataMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
} catch (SelfSendException e) {
|
||||
Log.i(TAG, "Dropping UD message from self.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,6 +552,10 @@ public class PushDecryptJob extends ContextJob {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceContactUpdateJob(getContext(), true));
|
||||
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new RefreshUnidentifiedDeliveryAbilityJob(context));
|
||||
}
|
||||
|
||||
if (message.isGroupsRequest()) {
|
||||
@@ -559,7 +573,9 @@ public class PushDecryptJob extends ContextJob {
|
||||
if (message.isConfigurationRequest()) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceReadReceiptUpdateJob(getContext(), TextSecurePreferences.isReadReceiptsEnabled(getContext())));
|
||||
.add(new MultiDeviceConfigurationUpdateJob(getContext(),
|
||||
TextSecurePreferences.isReadReceiptsEnabled(getContext()),
|
||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,7 +686,17 @@ public class PushDecryptJob extends ContextJob {
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||
long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, null);
|
||||
|
||||
if (recipients.getAddress().isGroup()) {
|
||||
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
||||
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipients.getAddress().toGroupString(), false);
|
||||
|
||||
for (Recipient member : members) {
|
||||
receiptDatabase.setUnidentified(member.getAddress(), messageId, message.isUnidentified(member.getAddress().serialize()));
|
||||
}
|
||||
}
|
||||
|
||||
database.markAsSent(messageId, true);
|
||||
database.markUnidentified(messageId, message.isUnidentified(recipients.getAddress().serialize()));
|
||||
|
||||
for (DatabaseAttachment attachment : DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId)) {
|
||||
ApplicationContext.getInstance(context)
|
||||
@@ -753,11 +779,19 @@ public class PushDecryptJob extends ContextJob {
|
||||
|
||||
messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, null);
|
||||
database = DatabaseFactory.getMmsDatabase(context);
|
||||
|
||||
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
||||
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), false);
|
||||
|
||||
for (Recipient member : members) {
|
||||
receiptDatabase.setUnidentified(member.getAddress(), messageId, message.isUnidentified(member.getAddress().serialize()));
|
||||
}
|
||||
} else {
|
||||
OutgoingTextMessage outgoingTextMessage = new OutgoingEncryptedMessage(recipient, body, expiresInMillis);
|
||||
|
||||
messageId = DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoingTextMessage, false, message.getTimestamp(), null);
|
||||
database = DatabaseFactory.getSmsDatabase(context);
|
||||
database.markUnidentified(messageId, message.isUnidentified(recipient.getAddress().serialize()));
|
||||
}
|
||||
|
||||
database.markAsSent(messageId, true);
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -99,6 +100,11 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAdded() {
|
||||
DatabaseFactory.getMmsDatabase(context).markAsSending(messageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushSend()
|
||||
throws IOException, MmsException, NoSuchMessageException, RetryLaterException
|
||||
@@ -110,6 +116,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
||||
|
||||
try {
|
||||
Log.i(TAG, "Sending message: " + messageId);
|
||||
|
||||
List<Address> target;
|
||||
|
||||
if (filterAddress != null) target = Collections.singletonList(Address.fromSerialized(filterAddress));
|
||||
|
||||
@@ -10,16 +10,19 @@ import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
@@ -68,6 +71,11 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
return dataBuilder.putLong(KEY_MESSAGE_ID, messageId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAdded() {
|
||||
DatabaseFactory.getMmsDatabase(context).markAsSending(messageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushSend()
|
||||
throws RetryLaterException, MmsException, NoSuchMessageException,
|
||||
@@ -85,6 +93,19 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
markAttachmentsUploaded(messageId, message.getAttachments());
|
||||
database.markUnidentified(messageId, unidentified);
|
||||
|
||||
if (TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
|
||||
Recipient recipient = message.getRecipient().resolve();
|
||||
UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode();
|
||||
|
||||
if (unidentified && (accessMode == UnidentifiedAccessMode.UNKNOWN || accessMode == UnidentifiedAccessMode.DISABLED)) {
|
||||
Log.i(TAG, "Marking recipient as UD-enabled following a UD send.");
|
||||
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED);
|
||||
} else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) {
|
||||
Log.i(TAG, "Marking recipient as UD-disabled following a non-UD send.");
|
||||
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
|
||||
database.markExpireStarted(messageId);
|
||||
expirationManager.scheduleDeletion(messageId, true, message.getExpiresIn());
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.jobs;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
@@ -19,11 +20,11 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||
@@ -68,6 +69,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
||||
@Override
|
||||
public void onAdded() {
|
||||
Log.i(TAG, "onAdded() messageId: " + messageId);
|
||||
DatabaseFactory.getSmsDatabase(context).markAsSending(messageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,6 +85,19 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
||||
database.markAsSent(messageId, true);
|
||||
database.markUnidentified(messageId, unidentified);
|
||||
|
||||
if (TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
|
||||
Recipient recipient = record.getRecipient().resolve();
|
||||
UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode();
|
||||
|
||||
if (unidentified && (accessMode == UnidentifiedAccessMode.UNKNOWN || accessMode == UnidentifiedAccessMode.DISABLED)) {
|
||||
Log.i(TAG, "Marking recipient as UD-enabled following a UD send.");
|
||||
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED);
|
||||
} else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) {
|
||||
Log.i(TAG, "Marking recipient as UD-disabled following a non-UD send.");
|
||||
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
if (record.getExpiresIn() > 0) {
|
||||
database.markExpireStarted(messageId);
|
||||
expirationManager.scheduleDeletion(record.getId(), record.isMms(), record.getExpiresIn());
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.jobs;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
@@ -10,10 +11,6 @@ import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureException;
|
||||
@@ -63,6 +60,10 @@ public class RefreshAttributesJob extends ContextJob implements InjectableType {
|
||||
|
||||
signalAccountManager.setAccountAttributes(signalingKey, registrationId, fetchesMessages, pin,
|
||||
unidentifiedAccessKey, universalUnidentifiedAccess);
|
||||
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new RefreshUnidentifiedDeliveryAbilityJob(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.work.Data;
|
||||
|
||||
public class RefreshUnidentifiedDeliveryAbilityJob extends ContextJob implements InjectableType {
|
||||
|
||||
private static final String TAG = RefreshUnidentifiedDeliveryAbilityJob.class.getSimpleName();
|
||||
|
||||
@Inject transient SignalServiceMessageReceiver receiver;
|
||||
|
||||
public RefreshUnidentifiedDeliveryAbilityJob() {
|
||||
super(null, null);
|
||||
}
|
||||
|
||||
public RefreshUnidentifiedDeliveryAbilityJob(Context context) {
|
||||
super(context, new JobParameters.Builder()
|
||||
.withNetworkRequirement()
|
||||
.create());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize(@NonNull SafeData data) { }
|
||||
|
||||
@Override
|
||||
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
|
||||
return dataBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws Exception {
|
||||
byte[] profileKey = ProfileKeyUtil.getProfileKey(context);
|
||||
SignalServiceProfile profile = retrieveProfile(TextSecurePreferences.getLocalNumber(context));
|
||||
|
||||
boolean enabled = profile.getUnidentifiedAccess() != null && isValidVerifier(profileKey, profile.getUnidentifiedAccess());
|
||||
|
||||
TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, enabled);
|
||||
Log.i(TAG, "Set UD status to: " + enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCanceled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onShouldRetry(Exception exception) {
|
||||
return exception instanceof PushNetworkException;
|
||||
}
|
||||
|
||||
private SignalServiceProfile retrieveProfile(@NonNull String number) throws IOException {
|
||||
SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe();
|
||||
|
||||
if (pipe != null) {
|
||||
try {
|
||||
return pipe.getProfile(new SignalServiceAddress(number), Optional.absent());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
return receiver.retrieveProfile(new SignalServiceAddress(number), Optional.absent());
|
||||
}
|
||||
|
||||
private boolean isValidVerifier(@NonNull byte[] profileKey, @NonNull String verifier) {
|
||||
ProfileCipher profileCipher = new ProfileCipher(profileKey);
|
||||
try {
|
||||
return profileCipher.verifyUnidentifiedAccess(Base64.decode(verifier));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -101,9 +101,8 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
|
||||
|
||||
try {
|
||||
profile = retrieveProfile(number, unidentifiedAccess);
|
||||
} catch (AuthorizationFailedException e) {
|
||||
} catch (NonSuccessfulResponseCodeException e) {
|
||||
if (unidentifiedAccess.isPresent()) {
|
||||
// XXX Update UI
|
||||
profile = retrieveProfile(number, Optional.absent());
|
||||
} else {
|
||||
throw e;
|
||||
@@ -129,7 +128,10 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
|
||||
private SignalServiceProfile retrieveProfile(@NonNull String number, Optional<UnidentifiedAccess> unidentifiedAccess)
|
||||
throws IOException
|
||||
{
|
||||
SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe();
|
||||
SignalServiceMessagePipe authPipe = IncomingMessageObserver.getPipe();
|
||||
SignalServiceMessagePipe unidentifiedPipe = IncomingMessageObserver.getUnidentifiedPipe();
|
||||
SignalServiceMessagePipe pipe = unidentifiedPipe != null && unidentifiedAccess.isPresent() ? unidentifiedPipe
|
||||
: authPipe;
|
||||
|
||||
if (pipe != null) {
|
||||
try {
|
||||
@@ -169,10 +171,11 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
|
||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||
byte[] profileKey = recipient.getProfileKey();
|
||||
|
||||
// XXX Update UI
|
||||
if (unrestrictedUnidentifiedAccess) {
|
||||
Log.i(TAG, "Marking recipient UD status as unrestricted.");
|
||||
recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED);
|
||||
} else if (profileKey == null || unidentifiedAccessVerifier == null) {
|
||||
Log.i(TAG, "Marking recipient UD status as disabled.");
|
||||
recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED);
|
||||
} else {
|
||||
ProfileCipher profileCipher = new ProfileCipher(profileKey);
|
||||
@@ -185,7 +188,9 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
|
||||
verifiedUnidentifiedAccess = false;
|
||||
}
|
||||
|
||||
recipientDatabase.setUnidentifiedAccessMode(recipient, verifiedUnidentifiedAccess ? UnidentifiedAccessMode.ENABLED : UnidentifiedAccessMode.DISABLED);
|
||||
UnidentifiedAccessMode mode = verifiedUnidentifiedAccess ? UnidentifiedAccessMode.ENABLED : UnidentifiedAccessMode.DISABLED;
|
||||
Log.i(TAG, "Marking recipient UD status as " + mode.name() + " after verification.");
|
||||
recipientDatabase.setUnidentifiedAccessMode(recipient, mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
88
src/org/thoughtcrime/securesms/jobs/RotateProfileKeyJob.java
Normal file
88
src/org/thoughtcrime/securesms/jobs/RotateProfileKeyJob.java
Normal file
@@ -0,0 +1,88 @@
|
||||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
import org.whispersystems.signalservice.api.util.StreamDetails;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.work.Data;
|
||||
|
||||
public class RotateProfileKeyJob extends ContextJob implements InjectableType {
|
||||
|
||||
@Inject SignalServiceAccountManager accountManager;
|
||||
|
||||
public RotateProfileKeyJob() {
|
||||
super(null, null);
|
||||
}
|
||||
|
||||
public RotateProfileKeyJob(Context context) {
|
||||
super(context, new JobParameters.Builder()
|
||||
.withGroupId("__ROTATE_PROFILE_KEY__")
|
||||
.withDuplicatesIgnored(true)
|
||||
.withNetworkRequirement()
|
||||
.create());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Data serialize(@NonNull Data.Builder dataBuilder) {
|
||||
return dataBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize(@NonNull SafeData data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws Exception {
|
||||
byte[] profileKey = ProfileKeyUtil.rotateProfileKey(context);
|
||||
|
||||
accountManager.setProfileName(profileKey, TextSecurePreferences.getProfileName(context));
|
||||
accountManager.setProfileAvatar(profileKey, getProfileAvatar());
|
||||
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new RefreshAttributesJob(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCanceled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onShouldRetry(Exception exception) {
|
||||
return exception instanceof PushNetworkException;
|
||||
}
|
||||
|
||||
private @Nullable StreamDetails getProfileAvatar() {
|
||||
try {
|
||||
Address localAddress = Address.fromSerialized(TextSecurePreferences.getLocalNumber(context));
|
||||
File avatarFile = AvatarHelper.getAvatarFile(context, localAddress);
|
||||
|
||||
if (avatarFile.exists()) {
|
||||
return new StreamDetails(new FileInputStream(avatarFile), "image/jpeg", avatarFile.length());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.preference.CheckBoxPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
@@ -20,9 +21,12 @@ import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceReadReceiptUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||
import org.thoughtcrime.securesms.lock.RegistrationLockDialog;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
|
||||
@@ -34,7 +38,8 @@ import mobi.upod.timedurationpicker.TimeDurationPickerDialog;
|
||||
|
||||
public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment implements InjectableType {
|
||||
|
||||
private static final String PREFERENCE_CATEGORY_BLOCKED = "preference_category_blocked";
|
||||
private static final String PREFERENCE_CATEGORY_BLOCKED = "preference_category_blocked";
|
||||
private static final String PREFERENCE_UNIDENTIFIED_LEARN_MORE = "pref_unidentified_learn_more";
|
||||
|
||||
private CheckBoxPreference disablePassphrase;
|
||||
|
||||
@@ -61,6 +66,9 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
||||
this.findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF).setOnPreferenceClickListener(new PassphraseIntervalClickListener());
|
||||
this.findPreference(TextSecurePreferences.READ_RECEIPTS_PREF).setOnPreferenceChangeListener(new ReadReceiptToggleListener());
|
||||
this.findPreference(PREFERENCE_CATEGORY_BLOCKED).setOnPreferenceClickListener(new BlockedContactsClickListener());
|
||||
this.findPreference(TextSecurePreferences.SHOW_UNIDENTIFIED_DELIVERY_INDICATORS).setOnPreferenceChangeListener(new ShowUnidentifiedDeliveryIndicatorsChangedListener());
|
||||
this.findPreference(TextSecurePreferences.UNIVERSAL_UNIDENTIFIED_ACCESS).setOnPreferenceChangeListener(new UniversalUnidentifiedAccessChangedListener());
|
||||
this.findPreference(PREFERENCE_UNIDENTIFIED_LEARN_MORE).setOnPreferenceClickListener(new UnidentifiedLearnMoreClickListener());
|
||||
disablePassphrase.setOnPreferenceChangeListener(new DisablePassphraseClickListener());
|
||||
|
||||
initializeVisibility();
|
||||
@@ -177,7 +185,9 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
||||
boolean enabled = (boolean)newValue;
|
||||
ApplicationContext.getInstance(getContext())
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceReadReceiptUpdateJob(getContext(), enabled));
|
||||
.add(new MultiDeviceConfigurationUpdateJob(getContext(),
|
||||
enabled,
|
||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext())));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -271,4 +281,35 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
||||
}
|
||||
}
|
||||
|
||||
private class ShowUnidentifiedDeliveryIndicatorsChangedListener implements Preference.OnPreferenceChangeListener {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
boolean enabled = (boolean) newValue;
|
||||
ApplicationContext.getInstance(getContext())
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceConfigurationUpdateJob(getContext(),
|
||||
TextSecurePreferences.isReadReceiptsEnabled(getContext()),
|
||||
enabled));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class UniversalUnidentifiedAccessChangedListener implements Preference.OnPreferenceChangeListener {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object o) {
|
||||
ApplicationContext.getInstance(getContext())
|
||||
.getJobManager()
|
||||
.add(new RefreshAttributesJob(getContext()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class UnidentifiedLearnMoreClickListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
CommunicationActions.openBrowserLink(preference.getContext(), "https://signal.org/blog/secret-sender/");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,4 @@ public class SecurityEventListener implements SignalServiceMessageSender.EventLi
|
||||
public void onSecurityEvent(SignalServiceAddress textSecureAddress) {
|
||||
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnidentifiedAccessFailed(SignalServiceAddress address) {
|
||||
// XXX Update UI
|
||||
DatabaseFactory.getRecipientDatabase(context)
|
||||
.setUnidentifiedAccessMode(Recipient.from(context, Address.fromSerialized(address.getNumber()), false),
|
||||
RecipientDatabase.UnidentifiedAccessMode.DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails;
|
||||
import org.thoughtcrime.securesms.util.FutureTaskListener;
|
||||
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public class IncomingMessageObserver implements InjectableType, RequirementListe
|
||||
private static final long REQUEST_TIMEOUT_MINUTES = 1;
|
||||
|
||||
private static SignalServiceMessagePipe pipe = null;
|
||||
public static SignalServiceMessagePipe unidentifiedPipe = null;
|
||||
private static SignalServiceMessagePipe unidentifiedPipe = null;
|
||||
|
||||
private final Context context;
|
||||
private final NetworkRequirement networkRequirement;
|
||||
@@ -158,7 +158,7 @@ public class IncomingMessageObserver implements InjectableType, RequirementListe
|
||||
Log.i(TAG, "Reading message...");
|
||||
localPipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES,
|
||||
envelope -> {
|
||||
Log.i(TAG, "Retrieved envelope! " + envelope.getSource());
|
||||
Log.i(TAG, "Retrieved envelope! " + String.valueOf(envelope.getSource()));
|
||||
new PushContentReceiveJob(context).processEnvelope(envelope);
|
||||
});
|
||||
} catch (TimeoutException e) {
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
package org.thoughtcrime.securesms.service;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.gcm.GcmBroadcastReceiver;
|
||||
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
|
||||
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirementProvider;
|
||||
import org.thoughtcrime.securesms.jobmanager.requirements.RequirementListener;
|
||||
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.InvalidVersionException;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class MessageRetrievalService extends Service implements InjectableType, RequirementListener {
|
||||
|
||||
private static final String TAG = MessageRetrievalService.class.getSimpleName();
|
||||
|
||||
public static final String ACTION_ACTIVITY_STARTED = "ACTIVITY_STARTED";
|
||||
public static final String ACTION_ACTIVITY_FINISHED = "ACTIVITY_FINISHED";
|
||||
public static final String ACTION_PUSH_RECEIVED = "PUSH_RECEIVED";
|
||||
public static final String ACTION_INITIALIZE = "INITIALIZE";
|
||||
public static final int FOREGROUND_ID = 313399;
|
||||
|
||||
private static final long REQUEST_TIMEOUT_MINUTES = 1;
|
||||
|
||||
private NetworkRequirement networkRequirement;
|
||||
private NetworkRequirementProvider networkRequirementProvider;
|
||||
|
||||
@Inject
|
||||
public SignalServiceMessageReceiver receiver;
|
||||
|
||||
private int activeActivities = 0;
|
||||
private List<Intent> pushPending = new LinkedList<>();
|
||||
private MessageRetrievalThread retrievalThread = null;
|
||||
|
||||
public static SignalServiceMessagePipe pipe = null;
|
||||
public static SignalServiceMessagePipe unidentifiedPipe = null;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
ApplicationContext.getInstance(this).injectDependencies(this);
|
||||
|
||||
networkRequirement = new NetworkRequirement(this);
|
||||
networkRequirementProvider = new NetworkRequirementProvider(this);
|
||||
|
||||
networkRequirementProvider.setListener(this);
|
||||
|
||||
retrievalThread = new MessageRetrievalThread();
|
||||
retrievalThread.start();
|
||||
|
||||
setForegroundIfNecessary();
|
||||
}
|
||||
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent == null) return START_STICKY;
|
||||
|
||||
if (ACTION_ACTIVITY_STARTED.equals(intent.getAction())) incrementActive();
|
||||
else if (ACTION_ACTIVITY_FINISHED.equals(intent.getAction())) decrementActive();
|
||||
else if (ACTION_PUSH_RECEIVED.equals(intent.getAction())) incrementPushReceived(intent);
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (retrievalThread != null) {
|
||||
retrievalThread.stopThread();
|
||||
}
|
||||
|
||||
sendBroadcast(new Intent("org.thoughtcrime.securesms.RESTART"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequirementStatusChanged() {
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setForegroundIfNecessary() {
|
||||
if (TextSecurePreferences.isGcmDisabled(this)) {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NotificationChannels.OTHER);
|
||||
builder.setContentTitle(getString(R.string.MessageRetrievalService_signal));
|
||||
builder.setContentText(getString(R.string.MessageRetrievalService_background_connection_enabled));
|
||||
builder.setPriority(NotificationCompat.PRIORITY_MIN);
|
||||
builder.setWhen(0);
|
||||
builder.setSmallIcon(R.drawable.ic_signal_grey_24dp);
|
||||
startForeground(FOREGROUND_ID, builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void incrementActive() {
|
||||
activeActivities++;
|
||||
Log.d(TAG, "Active Count: " + activeActivities);
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
private synchronized void decrementActive() {
|
||||
activeActivities--;
|
||||
Log.d(TAG, "Active Count: " + activeActivities);
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
private synchronized void incrementPushReceived(Intent intent) {
|
||||
pushPending.add(intent);
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
private synchronized void decrementPushReceived() {
|
||||
if (!pushPending.isEmpty()) {
|
||||
Intent intent = pushPending.remove(0);
|
||||
GcmBroadcastReceiver.completeWakefulIntent(intent);
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized boolean isConnectionNecessary() {
|
||||
boolean isGcmDisabled = TextSecurePreferences.isGcmDisabled(this);
|
||||
|
||||
Log.d(TAG, String.format("Network requirement: %s, active activities: %s, push pending: %s, gcm disabled: %b",
|
||||
networkRequirement.isPresent(), activeActivities, pushPending.size(), isGcmDisabled));
|
||||
|
||||
return TextSecurePreferences.isPushRegistered(this) &&
|
||||
TextSecurePreferences.isWebsocketRegistered(this) &&
|
||||
(activeActivities > 0 || !pushPending.isEmpty() || isGcmDisabled) &&
|
||||
networkRequirement.isPresent();
|
||||
}
|
||||
|
||||
private synchronized void waitForConnectionNecessary() {
|
||||
try {
|
||||
while (!isConnectionNecessary()) wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdown(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) {
|
||||
try {
|
||||
pipe.shutdown();
|
||||
unidentifiedPipe.shutdown();
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, t);
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerActivityStarted(Context activity) {
|
||||
Intent intent = new Intent(activity, MessageRetrievalService.class);
|
||||
intent.setAction(MessageRetrievalService.ACTION_ACTIVITY_STARTED);
|
||||
activity.startService(intent);
|
||||
}
|
||||
|
||||
public static void registerActivityStopped(Context activity) {
|
||||
Intent intent = new Intent(activity, MessageRetrievalService.class);
|
||||
intent.setAction(MessageRetrievalService.ACTION_ACTIVITY_FINISHED);
|
||||
activity.startService(intent);
|
||||
}
|
||||
|
||||
public static @Nullable SignalServiceMessagePipe getPipe() {
|
||||
return pipe;
|
||||
}
|
||||
|
||||
public static @Nullable SignalServiceMessagePipe getUnidentifiedPipe() {
|
||||
return unidentifiedPipe;
|
||||
}
|
||||
|
||||
private class MessageRetrievalThread extends Thread implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private AtomicBoolean stopThread = new AtomicBoolean(false);
|
||||
|
||||
MessageRetrievalThread() {
|
||||
super("MessageRetrievalService");
|
||||
setUncaughtExceptionHandler(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!stopThread.get()) {
|
||||
Log.i(TAG, "Waiting for websocket state change....");
|
||||
waitForConnectionNecessary();
|
||||
|
||||
Log.i(TAG, "Making websocket connection....");
|
||||
pipe = receiver.createMessagePipe();
|
||||
unidentifiedPipe = receiver.createUnidentifiedMessagePipe();
|
||||
|
||||
SignalServiceMessagePipe localPipe = pipe;
|
||||
SignalServiceMessagePipe unidentifiedLocalPipe = unidentifiedPipe;
|
||||
|
||||
try {
|
||||
while (isConnectionNecessary() && !stopThread.get()) {
|
||||
try {
|
||||
Log.i(TAG, "Reading message...");
|
||||
localPipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES,
|
||||
envelope -> {
|
||||
Log.i(TAG, "Retrieved envelope! " + envelope.getSource());
|
||||
new PushContentReceiveJob(getApplicationContext()).processEnvelope(envelope);
|
||||
decrementPushReceived();
|
||||
});
|
||||
} catch (TimeoutException e) {
|
||||
Log.w(TAG, "Application level read timeout...");
|
||||
} catch (InvalidVersionException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.w(TAG, e);
|
||||
} finally {
|
||||
Log.w(TAG, "Shutting down pipe...");
|
||||
shutdown(localPipe, unidentifiedLocalPipe);
|
||||
}
|
||||
|
||||
Log.i(TAG, "Looping...");
|
||||
}
|
||||
|
||||
Log.i(TAG, "Exiting...");
|
||||
}
|
||||
|
||||
private void stopThread() {
|
||||
stopThread.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
Log.w(TAG, "*** Uncaught exception!");
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
@@ -10,6 +11,7 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
@@ -87,4 +89,13 @@ public class CommunicationActions {
|
||||
}
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void openBrowserLink(@NonNull Context context, @NonNull String link) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
|
||||
context.startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(context, R.string.CommunicationActions_no_browser_found, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,10 +164,12 @@ public class TextSecurePreferences {
|
||||
private static final String NOTIFICATION_MESSAGES_CHANNEL_VERSION = "pref_notification_messages_channel_version";
|
||||
|
||||
private static final String NEEDS_MESSAGE_PULL = "pref_needs_message_pull";
|
||||
|
||||
|
||||
private static final String UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF = "pref_unidentified_access_certificate_rotation_time";
|
||||
private static final String UNIDENTIFIED_ACCESS_CERTIFICATE = "pref_unidentified_access_certificate";
|
||||
private static final String UNIVERSAL_UNIDENTIFIED_ACCESS = "pref_universal_unidentified_access";
|
||||
public static final String UNIVERSAL_UNIDENTIFIED_ACCESS = "pref_universal_unidentified_access";
|
||||
public static final String SHOW_UNIDENTIFIED_DELIVERY_INDICATORS = "pref_show_unidentifed_delivery_indicators";
|
||||
private static final String UNIDENTIFIED_DELIVERY_ENABLED = "pref_unidentified_delivery_enabled";
|
||||
|
||||
public static boolean isScreenLockEnabled(@NonNull Context context) {
|
||||
return getBooleanPreference(context, SCREEN_LOCK, false);
|
||||
@@ -540,8 +542,16 @@ public class TextSecurePreferences {
|
||||
return getBooleanPreference(context, UNIVERSAL_UNIDENTIFIED_ACCESS, false);
|
||||
}
|
||||
|
||||
public static void setUniversalUnidentifiedAccess(Context context, boolean value) {
|
||||
setBooleanPreference(context, UNIVERSAL_UNIDENTIFIED_ACCESS, value);
|
||||
public static boolean isShowUnidentifiedDeliveryIndicatorsEnabled(Context context) {
|
||||
return getBooleanPreference(context, SHOW_UNIDENTIFIED_DELIVERY_INDICATORS, false);
|
||||
}
|
||||
|
||||
public static void setIsUnidentifiedDeliveryEnabled(Context context, boolean enabled) {
|
||||
setBooleanPreference(context, UNIDENTIFIED_DELIVERY_ENABLED, enabled);
|
||||
}
|
||||
|
||||
public static boolean isUnidentifiedDeliveryEnabled(Context context) {
|
||||
return getBooleanPreference(context, UNIDENTIFIED_DELIVERY_ENABLED, true);
|
||||
}
|
||||
|
||||
public static long getSignedPreKeyRotationTime(Context context) {
|
||||
|
||||
Reference in New Issue
Block a user