Skip to content

Commit 57c0b8f

Browse files
Initial pre-alpha support for sender key.
1 parent c54f016 commit 57c0b8f

File tree

124 files changed

+3662
-438
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

124 files changed

+3662
-438
lines changed

app/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ dependencies {
376376
implementation project(':device-transfer')
377377

378378
implementation 'org.signal:zkgroup-android:0.7.0'
379-
implementation 'org.whispersystems:signal-client-android:0.5.1'
379+
implementation 'org.whispersystems:signal-client-android:0.8.0'
380380
implementation 'com.google.protobuf:protobuf-javalite:3.10.0'
381381

382382
implementation('com.mobilecoin:android-sdk:1.0.0') {

app/src/main/AndroidManifest.xml

+2
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,8 @@
736736

737737
<receiver android:name=".revealable.ViewOnceMessageManager$ViewOnceAlarm" />
738738

739+
<receiver android:name=".service.PendingRetryReceiptManager$PendingRetryReceiptAlarm" />
740+
739741
<receiver android:name=".service.TrimThreadsByDateManager$TrimThreadsByDateAlarm" />
740742

741743
<receiver android:name=".payments.backup.phrase.ClearClipboardAlarmReceiver" />

app/src/main/java/org/thoughtcrime/securesms/AppCapabilities.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.thoughtcrime.securesms;
22

3-
import org.thoughtcrime.securesms.util.FeatureFlags;
43
import org.whispersystems.signalservice.api.account.AccountAttributes;
54

65
public final class AppCapabilities {
@@ -11,12 +10,13 @@ private AppCapabilities() {
1110
private static final boolean UUID_CAPABLE = false;
1211
private static final boolean GV2_CAPABLE = true;
1312
private static final boolean GV1_MIGRATION = true;
13+
private static final boolean SENDER_KEY = true;
1414

1515
/**
1616
* @param storageCapable Whether or not the user can use storage service. This is another way of
1717
* asking if the user has set a Signal PIN or not.
1818
*/
1919
public static AccountAttributes.Capabilities getCapabilities(boolean storageCapable) {
20-
return new AccountAttributes.Capabilities(UUID_CAPABLE, GV2_CAPABLE, storageCapable, GV1_MIGRATION);
20+
return new AccountAttributes.Capabilities(UUID_CAPABLE, GV2_CAPABLE, storageCapable, GV1_MIGRATION, SENDER_KEY);
2121
}
2222
}

app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java

+5
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ public void onCreate() {
146146
.addBlocking("blob-provider", this::initializeBlobProvider)
147147
.addBlocking("feature-flags", FeatureFlags::init)
148148
.addNonBlocking(this::initializeRevealableMessageManager)
149+
.addNonBlocking(this::initializePendingRetryReceiptManager)
149150
.addNonBlocking(this::initializeGcmCheck)
150151
.addNonBlocking(this::initializeSignedPreKeyCheck)
151152
.addNonBlocking(this::initializePeriodicTasks)
@@ -300,6 +301,10 @@ private void initializeRevealableMessageManager() {
300301
ApplicationDependencies.getViewOnceMessageManager().scheduleIfNecessary();
301302
}
302303

304+
private void initializePendingRetryReceiptManager() {
305+
ApplicationDependencies.getPendingRetryReceiptManager().scheduleIfNecessary();
306+
}
307+
303308
private void initializePeriodicTasks() {
304309
RotateSignedPreKeyListener.schedule(this);
305310
DirectoryRefreshListener.schedule(this);

app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ interface EventListener {
7272
void onVoiceNotePlay(@NonNull Uri uri, long messageId, double position);
7373
void onVoiceNoteSeekTo(@NonNull Uri uri, double position);
7474
void onGroupMigrationLearnMoreClicked(@NonNull GroupMigrationMembershipChange membershipChange);
75-
void onDecryptionFailedLearnMoreClicked();
75+
void onChatSessionRefreshLearnMoreClicked();
76+
void onBadDecryptLearnMoreClicked(@NonNull RecipientId author);
7677
void onSafetyNumberLearnMoreClicked(@NonNull Recipient recipient);
7778
void onJoinGroupCallClicked();
7879
void onInviteFriendsToGroupClicked(@NonNull GroupId.V2 groupId);

app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
import org.thoughtcrime.securesms.database.MmsDatabase;
3131
import org.thoughtcrime.securesms.database.MmsSmsColumns;
3232
import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase;
33+
import org.thoughtcrime.securesms.database.PendingRetryReceiptDatabase;
3334
import org.thoughtcrime.securesms.database.SearchDatabase;
35+
import org.thoughtcrime.securesms.database.SenderKeyDatabase;
36+
import org.thoughtcrime.securesms.database.SenderKeySharedDatabase;
3437
import org.thoughtcrime.securesms.database.SessionDatabase;
3538
import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
3639
import org.thoughtcrime.securesms.database.SmsDatabase;
@@ -39,6 +42,7 @@
3942
import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet;
4043
import org.thoughtcrime.securesms.keyvalue.SignalStore;
4144
import org.thoughtcrime.securesms.profiles.AvatarHelper;
45+
import org.thoughtcrime.securesms.service.PendingRetryReceiptManager;
4246
import org.thoughtcrime.securesms.util.SetUtil;
4347
import org.thoughtcrime.securesms.util.Stopwatch;
4448
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -77,7 +81,10 @@ public class FullBackupExporter extends FullBackupBase {
7781
SessionDatabase.TABLE_NAME,
7882
SearchDatabase.SMS_FTS_TABLE_NAME,
7983
SearchDatabase.MMS_FTS_TABLE_NAME,
80-
EmojiSearchDatabase.TABLE_NAME
84+
EmojiSearchDatabase.TABLE_NAME,
85+
SenderKeyDatabase.TABLE_NAME,
86+
SenderKeySharedDatabase.TABLE_NAME,
87+
PendingRetryReceiptDatabase.TABLE_NAME
8188
);
8289

8390
public static void export(@NonNull Context context,

app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt

+41
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
1414
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
1515
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
1616
import org.thoughtcrime.securesms.components.settings.configure
17+
import org.thoughtcrime.securesms.database.DatabaseFactory
1718
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
1819
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
1920
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob
@@ -212,6 +213,35 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
212213
viewModel.setDisableAutoMigrationNotification(!state.useBuiltInEmojiSet)
213214
}
214215
)
216+
217+
dividerPref()
218+
219+
sectionHeaderPref(R.string.preferences__internal_sender_key)
220+
221+
clickPref(
222+
title = DSLSettingsText.from(R.string.preferences__internal_clear_all_state),
223+
summary = DSLSettingsText.from(R.string.preferences__internal_click_to_delete_all_sender_key_state),
224+
onClick = {
225+
clearAllSenderKeyState()
226+
}
227+
)
228+
229+
clickPref(
230+
title = DSLSettingsText.from(R.string.preferences__internal_clear_shared_state),
231+
summary = DSLSettingsText.from(R.string.preferences__internal_click_to_delete_all_sharing_state),
232+
onClick = {
233+
clearAllSenderKeySharedState()
234+
}
235+
)
236+
237+
switchPref(
238+
title = DSLSettingsText.from(R.string.preferences__internal_remove_two_person_minimum),
239+
summary = DSLSettingsText.from(R.string.preferences__internal_remove_the_requirement_that_you_need),
240+
isChecked = state.removeSenderKeyMinimium,
241+
onClick = {
242+
viewModel.setRemoveSenderKeyMinimum(!state.removeSenderKeyMinimium)
243+
}
244+
)
215245
}
216246
}
217247

@@ -278,4 +308,15 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
278308
ConversationUtil.clearAllShortcuts(requireContext())
279309
Toast.makeText(context, "Deleted all dynamic shortcuts.", Toast.LENGTH_SHORT).show()
280310
}
311+
312+
private fun clearAllSenderKeyState() {
313+
DatabaseFactory.getSenderKeyDatabase(requireContext()).deleteAll()
314+
DatabaseFactory.getSenderKeySharedDatabase(requireContext()).deleteAll()
315+
Toast.makeText(context, "Deleted all sender key state.", Toast.LENGTH_SHORT).show()
316+
}
317+
318+
private fun clearAllSenderKeySharedState() {
319+
DatabaseFactory.getSenderKeySharedDatabase(requireContext()).deleteAll()
320+
Toast.makeText(context, "Deleted all sender key shared state.", Toast.LENGTH_SHORT).show()
321+
}
281322
}

app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsState.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ data class InternalSettingsState(
1212
val disableAutoMigrationNotification: Boolean,
1313
val forceCensorship: Boolean,
1414
val useBuiltInEmojiSet: Boolean,
15-
val emojiVersion: EmojiFiles.Version?
15+
val emojiVersion: EmojiFiles.Version?,
16+
val removeSenderKeyMinimium: Boolean,
1617
)

app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
6565
refresh()
6666
}
6767

68+
fun setRemoveSenderKeyMinimum(enabled: Boolean) {
69+
preferenceDataStore.putBoolean(InternalValues.REMOVE_SENDER_KEY_MINIMUM, enabled)
70+
refresh()
71+
}
72+
6873
private fun refresh() {
6974
store.update { getState().copy(emojiVersion = it.emojiVersion) }
7075
}
@@ -79,7 +84,8 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
7984
disableAutoMigrationNotification = SignalStore.internalValues().disableGv1AutoMigrateNotification(),
8085
forceCensorship = SignalStore.internalValues().forcedCensorship(),
8186
useBuiltInEmojiSet = SignalStore.internalValues().forceBuiltInEmoji(),
82-
emojiVersion = null
87+
emojiVersion = null,
88+
removeSenderKeyMinimium = SignalStore.internalValues().removeSenderKeyMinimum()
8389
)
8490

8591
class Factory(private val repository: InternalSettingsRepository) : ViewModelProvider.Factory {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.thoughtcrime.securesms.conversation;
2+
3+
import android.app.Dialog;
4+
import android.os.Bundle;
5+
import android.view.LayoutInflater;
6+
import android.view.View;
7+
import android.widget.TextView;
8+
9+
import androidx.annotation.NonNull;
10+
import androidx.fragment.app.DialogFragment;
11+
import androidx.fragment.app.FragmentManager;
12+
13+
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
14+
15+
import org.signal.core.util.logging.Log;
16+
import org.thoughtcrime.securesms.R;
17+
import org.thoughtcrime.securesms.database.model.MessageRecord;
18+
import org.thoughtcrime.securesms.recipients.Recipient;
19+
import org.thoughtcrime.securesms.recipients.RecipientId;
20+
21+
/**
22+
* A dialog fragment that shows when you click 'learn more' on a {@link MessageRecord#isBadDecryptType()}.
23+
*/
24+
public final class BadDecryptLearnMoreDialog extends DialogFragment {
25+
26+
private static final String TAG = Log.tag(BadDecryptLearnMoreDialog.class);
27+
private static final String FRAGMENT_TAG = "BadDecryptLearnMoreDialog";
28+
29+
private static final String KEY_DISPLAY_NAME = "display_name";
30+
private static final String KEY_GROUP_CHAT = "group_chat";
31+
32+
public static void show(@NonNull FragmentManager fragmentManager, @NonNull String displayName, boolean isGroupChat) {
33+
if (fragmentManager.findFragmentByTag(FRAGMENT_TAG) != null) {
34+
Log.i(TAG, "Already shown!");
35+
return;
36+
}
37+
38+
Bundle args = new Bundle();
39+
args.putString(KEY_DISPLAY_NAME, displayName);
40+
args.putBoolean(KEY_GROUP_CHAT, isGroupChat);
41+
42+
BadDecryptLearnMoreDialog fragment = new BadDecryptLearnMoreDialog();
43+
fragment.setArguments(args);
44+
fragment.show(fragmentManager, FRAGMENT_TAG);
45+
}
46+
47+
@Override
48+
public Dialog onCreateDialog(Bundle savedInstanceState) {
49+
MaterialAlertDialogBuilder dialogBuilder = new MaterialAlertDialogBuilder(requireContext());
50+
51+
View view = LayoutInflater.from(requireContext()).inflate(R.layout.bad_decrypt_learn_more_dialog_fragment, null);
52+
TextView body = view.findViewById(R.id.bad_decrypt_dialog_body);
53+
54+
String displayName = requireArguments().getString(KEY_DISPLAY_NAME);
55+
boolean isGroup = requireArguments().getBoolean(KEY_GROUP_CHAT);
56+
57+
if (isGroup) {
58+
body.setText(getString(R.string.BadDecryptLearnMoreDialog_couldnt_be_delivered_group, displayName));
59+
} else {
60+
body.setText(getString(R.string.BadDecryptLearnMoreDialog_couldnt_be_delivered_individual, displayName));
61+
}
62+
63+
dialogBuilder.setView(view)
64+
.setPositiveButton(android.R.string.ok, null);
65+
66+
return dialogBuilder.create();
67+
}
68+
}

app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -1605,7 +1605,7 @@ public void onGroupMigrationLearnMoreClicked(@NonNull GroupMigrationMembershipCh
16051605
}
16061606

16071607
@Override
1608-
public void onDecryptionFailedLearnMoreClicked() {
1608+
public void onChatSessionRefreshLearnMoreClicked() {
16091609
new AlertDialog.Builder(requireContext())
16101610
.setView(R.layout.decryption_failed_dialog)
16111611
.setPositiveButton(android.R.string.ok, (d, w) -> {
@@ -1618,6 +1618,13 @@ public void onDecryptionFailedLearnMoreClicked() {
16181618
.show();
16191619
}
16201620

1621+
@Override
1622+
public void onBadDecryptLearnMoreClicked(@NonNull RecipientId author) {
1623+
SimpleTask.run(getLifecycle(),
1624+
() -> Recipient.resolved(author).getDisplayName(requireContext()),
1625+
name -> BadDecryptLearnMoreDialog.show(getParentFragmentManager(), name, recipient.get().isGroup()));
1626+
}
1627+
16211628
@Override
16221629
public void onSafetyNumberLearnMoreClicked(@NonNull Recipient recipient) {
16231630
if (recipient.isGroup()) {

app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -292,14 +292,14 @@ private void present(@NonNull ConversationMessage conversationMessage,
292292
eventListener.onGroupMigrationLearnMoreClicked(conversationMessage.getMessageRecord().getGroupV1MigrationMembershipChanges());
293293
}
294294
});
295-
} else if (conversationMessage.getMessageRecord().isFailedDecryptionType() &&
296-
(!nextMessageRecord.isPresent() || !nextMessageRecord.get().isFailedDecryptionType()))
295+
} else if (conversationMessage.getMessageRecord().isChatSessionRefresh() &&
296+
(!nextMessageRecord.isPresent() || !nextMessageRecord.get().isChatSessionRefresh()))
297297
{
298298
actionButton.setText(R.string.ConversationUpdateItem_learn_more);
299299
actionButton.setVisibility(VISIBLE);
300300
actionButton.setOnClickListener(v -> {
301301
if (batchSelected.isEmpty() && eventListener != null) {
302-
eventListener.onDecryptionFailedLearnMoreClicked();
302+
eventListener.onChatSessionRefreshLearnMoreClicked();
303303
}
304304
});
305305
} else if (conversationMessage.getMessageRecord().isIdentityUpdate()) {
@@ -370,6 +370,16 @@ private void present(@NonNull ConversationMessage conversationMessage,
370370
eventListener.onViewGroupDescriptionChange(conversationRecipient.getGroupId().orNull(), conversationMessage.getMessageRecord().getGroupV2DescriptionUpdate(), isMessageRequestAccepted);
371371
}
372372
});
373+
} else if (conversationMessage.getMessageRecord().isBadDecryptType() &&
374+
(!nextMessageRecord.isPresent() || !nextMessageRecord.get().isBadDecryptType()))
375+
{
376+
actionButton.setText(R.string.ConversationUpdateItem_learn_more);
377+
actionButton.setVisibility(VISIBLE);
378+
actionButton.setOnClickListener(v -> {
379+
if (batchSelected.isEmpty() && eventListener != null) {
380+
eventListener.onBadDecryptLearnMoreClicked(conversationMessage.getMessageRecord().getRecipient().getId());
381+
}
382+
});
373383
} else {
374384
actionButton.setVisibility(GONE);
375385
actionButton.setOnClickListener(null);

app/src/main/java/org/thoughtcrime/securesms/conversation/MenuState.java

+9-9
Original file line numberDiff line numberDiff line change
@@ -134,17 +134,17 @@ static boolean canReplyToMessage(@NonNull Recipient conversationRecipient, boole
134134
}
135135

136136
static boolean isActionMessage(@NonNull MessageRecord messageRecord) {
137-
return messageRecord.isGroupAction() ||
138-
messageRecord.isCallLog() ||
139-
messageRecord.isJoined() ||
137+
return messageRecord.isGroupAction() ||
138+
messageRecord.isCallLog() ||
139+
messageRecord.isJoined() ||
140140
messageRecord.isExpirationTimerUpdate() ||
141-
messageRecord.isEndSession() ||
142-
messageRecord.isIdentityUpdate() ||
143-
messageRecord.isIdentityVerified() ||
144-
messageRecord.isIdentityDefault() ||
145-
messageRecord.isProfileChange() ||
141+
messageRecord.isEndSession() ||
142+
messageRecord.isIdentityUpdate() ||
143+
messageRecord.isIdentityVerified() ||
144+
messageRecord.isIdentityDefault() ||
145+
messageRecord.isProfileChange() ||
146146
messageRecord.isGroupV1MigrationEvent() ||
147-
messageRecord.isFailedDecryptionType() ||
147+
messageRecord.isChatSessionRefresh() ||
148148
messageRecord.isInMemoryMessageRecord();
149149
}
150150

app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java

+10-5
Original file line numberDiff line numberDiff line change
@@ -354,10 +354,13 @@ private void setThumbnailSnippet(ThreadRecord thread) {
354354
}
355355

356356
private void setStatusIcons(ThreadRecord thread) {
357-
if (!thread.isOutgoing() ||
358-
thread.isOutgoingAudioCall() ||
359-
thread.isOutgoingVideoCall() ||
360-
thread.isVerificationStatusChange())
357+
if (MmsSmsColumns.Types.isBadDecryptType(thread.getType())) {
358+
deliveryStatusIndicator.setNone();
359+
alertView.setFailed();
360+
} else if (!thread.isOutgoing() ||
361+
thread.isOutgoingAudioCall() ||
362+
thread.isOutgoingVideoCall() ||
363+
thread.isVerificationStatusChange())
361364
{
362365
deliveryStatusIndicator.setNone();
363366
alertView.setNone();
@@ -435,7 +438,7 @@ public void onRecipientChanged(@NonNull Recipient recipient) {
435438
return emphasisAdded(context, context.getString(R.string.ThreadRecord_left_the_group), defaultTint);
436439
} else if (SmsDatabase.Types.isKeyExchangeType(thread.getType())) {
437440
return emphasisAdded(context, context.getString(R.string.ConversationListItem_key_exchange_message), defaultTint);
438-
} else if (SmsDatabase.Types.isFailedDecryptType(thread.getType())) {
441+
} else if (SmsDatabase.Types.isChatSessionRefresh(thread.getType())) {
439442
UpdateDescription description = UpdateDescription.staticDescription(context.getString(R.string.ThreadRecord_chat_session_refreshed), R.drawable.ic_refresh_16);
440443
return emphasisAdded(context, description, defaultTint);
441444
} else if (SmsDatabase.Types.isNoRemoteSessionType(thread.getType())) {
@@ -482,6 +485,8 @@ public void onRecipientChanged(@NonNull Recipient recipient) {
482485
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_could_not_be_processed), defaultTint);
483486
} else if (SmsDatabase.Types.isProfileChange(thread.getType())) {
484487
return emphasisAdded(context, "", defaultTint);
488+
} else if (MmsSmsColumns.Types.isBadDecryptType(thread.getType())) {
489+
return emphasisAdded(context, context.getString(R.string.ThreadRecord_delivery_issue), defaultTint);
485490
} else {
486491
ThreadDatabase.Extra extra = thread.getExtra();
487492
if (extra != null && extra.isViewOnce()) {

0 commit comments

Comments
 (0)