Add ability to hide contacts behind a feature flag.

This commit is contained in:
Alex Hart
2022-09-27 16:40:27 -03:00
committed by Cody Henthorne
parent a8a773db43
commit 04eeb434c9
19 changed files with 511 additions and 45 deletions

View File

@@ -20,11 +20,27 @@ import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import org.signal.core.util.DimensionUnit;
import org.signal.core.util.concurrent.SimpleTask;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.menu.ActionItem;
import org.thoughtcrime.securesms.components.menu.SignalContextMenu;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
import org.thoughtcrime.securesms.contacts.management.ContactsManagementRepository;
import org.thoughtcrime.securesms.contacts.management.ContactsManagementViewModel;
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.database.SignalDatabase;
@@ -33,32 +49,57 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.signal.core.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.LifecycleDisposable;
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Activity container for starting a new conversation.
*
* @author Moxie Marlinspike
*
*/
public class NewConversationActivity extends ContactSelectionActivity
implements ContactSelectionListFragment.ListCallback
implements ContactSelectionListFragment.ListCallback, ContactSelectionListFragment.OnItemLongClickListener
{
@SuppressWarnings("unused")
private static final String TAG = Log.tag(NewConversationActivity.class);
private ContactsManagementViewModel viewModel;
private ActivityResultLauncher<Intent> contactLauncher;
private final LifecycleDisposable disposables = new LifecycleDisposable();
@Override
public void onCreate(Bundle bundle, boolean ready) {
super.onCreate(bundle, ready);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(R.string.NewConversationActivity__new_message);
disposables.bindTo(this);
ContactsManagementRepository repository = new ContactsManagementRepository(this);
ContactsManagementViewModel.Factory factory = new ContactsManagementViewModel.Factory(repository);
contactLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
if (activityResult.getResultCode() == RESULT_OK) {
handleManualRefresh();
}
});
viewModel = new ViewModelProvider(this, factory).get(ContactsManagementViewModel.class);
}
@Override
@@ -120,10 +161,18 @@ public class NewConversationActivity extends ContactSelectionActivity
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case android.R.id.home: super.onBackPressed(); return true;
case R.id.menu_refresh: handleManualRefresh(); return true;
case R.id.menu_new_group: handleCreateGroup(); return true;
case R.id.menu_invite: handleInvite(); return true;
case android.R.id.home:
super.onBackPressed();
return true;
case R.id.menu_refresh:
handleManualRefresh();
return true;
case R.id.menu_new_group:
handleCreateGroup();
return true;
case R.id.menu_invite:
handleInvite();
return true;
}
return false;
@@ -162,4 +211,143 @@ public class NewConversationActivity extends ContactSelectionActivity
handleCreateGroup();
finish();
}
@Override
public boolean onLongClick(ContactSelectionListItem contactSelectionListItem) {
RecipientId recipientId = contactSelectionListItem.getRecipientId().orElse(null);
if (recipientId == null) {
return false;
}
List<ActionItem> actions = generateContextualActionsForRecipient(recipientId);
if (actions.isEmpty()) {
return false;
}
new SignalContextMenu.Builder(contactSelectionListItem, (ViewGroup) contactSelectionListItem.getRootView())
.preferredVerticalPosition(SignalContextMenu.VerticalPosition.BELOW)
.preferredHorizontalPosition(SignalContextMenu.HorizontalPosition.START)
.offsetX((int) DimensionUnit.DP.toPixels(12))
.offsetY((int) DimensionUnit.DP.toPixels(12))
.show(actions);
return true;
}
private @NonNull List<ActionItem> generateContextualActionsForRecipient(@NonNull RecipientId recipientId) {
Recipient recipient = Recipient.resolved(recipientId);
return Stream.of(
createMessageActionItem(recipient),
createAudioCallActionItem(recipient),
createVideoCallActionItem(recipient),
createRemoveActionItem(recipient),
createBlockActionItem(recipient)
).filter(Objects::nonNull).collect(Collectors.toList());
}
private @NonNull ActionItem createMessageActionItem(@NonNull Recipient recipient) {
return new ActionItem(
R.drawable.ic_message_24,
getString(R.string.NewConversationActivity__message),
R.color.signal_colorOnSurface,
() -> startActivity(ConversationIntents.createBuilder(this, recipient.getId(), -1L).build())
);
}
private @Nullable ActionItem createAudioCallActionItem(@NonNull Recipient recipient) {
if (recipient.isSelf() || recipient.isGroup()) {
return null;
}
return new ActionItem(
R.drawable.ic_phone_right_24,
getString(R.string.NewConversationActivity__audio_call),
R.color.signal_colorOnSurface,
() -> CommunicationActions.startVoiceCall(this, recipient)
);
}
private @Nullable ActionItem createVideoCallActionItem(@NonNull Recipient recipient) {
if (recipient.isSelf() || recipient.isMmsGroup()) {
return null;
}
return new ActionItem(
R.drawable.ic_video_call_24,
getString(R.string.NewConversationActivity__video_call),
R.color.signal_colorOnSurface,
() -> CommunicationActions.startVideoCall(this, recipient)
);
}
private @Nullable ActionItem createRemoveActionItem(@NonNull Recipient recipient) {
if (!FeatureFlags.hideContacts() || recipient.isSelf() || recipient.isGroup()) {
return null;
}
return new ActionItem(
R.drawable.ic_minus_circle_20, // TODO [alex] -- correct asset
getString(R.string.NewConversationActivity__remove),
R.color.signal_colorOnSurface,
() -> {
if (recipient.isSystemContact()) {
displayIsInSystemContactsDialog(recipient);
} else {
displayRemovalDialog(recipient);
}
}
);
}
@SuppressWarnings("CodeBlock2Expr")
private @Nullable ActionItem createBlockActionItem(@NonNull Recipient recipient) {
if (recipient.isSelf()) {
return null;
}
return new ActionItem(
R.drawable.ic_block_tinted_24,
getString(R.string.NewConversationActivity__block),
R.color.signal_colorError,
() -> BlockUnblockDialog.showBlockFor(this,
this.getLifecycle(),
recipient,
() -> {
disposables.add(viewModel.blockContact(recipient).subscribe(() -> {
displaySnackbar(R.string.NewConversationActivity__s_has_been_removed);
}));
})
);
}
private void displayIsInSystemContactsDialog(@NonNull Recipient recipient) {
new MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.NewConversationActivity__unable_to_remove_s, recipient.getShortDisplayName(this)))
.setMessage(R.string.NewConversationActivity__this_person_is_saved_to_your)
.setPositiveButton(R.string.NewConversationActivity__view_contact,
(dialog, which) -> contactLauncher.launch(new Intent(Intent.ACTION_VIEW, recipient.getContactUri()))
)
.setNegativeButton(android.R.string.cancel, null)
.show();
}
private void displayRemovalDialog(@NonNull Recipient recipient) {
new MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.NewConversationActivity__remove_s, recipient.getShortDisplayName(this)))
.setMessage(R.string.NewConversationActivity__you_wont_see_this_person)
.setPositiveButton(R.string.NewConversationActivity__remove,
(dialog, which) -> {
disposables.add(viewModel.hideContact(recipient).subscribe(() -> {
displaySnackbar(R.string.NewConversationActivity__s_has_been_removed);
}));
}
)
.setNegativeButton(android.R.string.cancel, null)
.show();
}
private void displaySnackbar(@StringRes int message) {
Snackbar.make(findViewById(android.R.id.content), message, Snackbar.LENGTH_SHORT).show();
}
}