diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 1a970cfdcd..41dcbb7c8a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -207,6 +207,7 @@ import org.thoughtcrime.securesms.recipients.RecipientExporter; import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientUtil; +import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment; import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.sms.MessageSender; @@ -594,7 +595,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity handleImageFromDeviceCameraApp(); break; case ADD_CONTACT: - onRecipientChanged(recipient.get()); + SimpleTask.run(() -> { + try { + DirectoryHelper.refreshDirectoryFor(this, recipient.get(), false); + } catch (IOException e) { + Log.w(TAG, "Failed to refresh user after adding to contacts."); + } + return null; + }, nothing -> onRecipientChanged(recipient.get())); break; case PICK_LOCATION: SignalPlace place = new SignalPlace(PlacePickerActivity.addressFromData(data)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java index ecce8cb4a3..4e2616e5f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java @@ -1,5 +1,7 @@ package org.thoughtcrime.securesms.recipients.ui.bottomsheet; +import android.app.Activity; +import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import android.view.LayoutInflater; @@ -20,6 +22,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.groups.GroupId; +import org.thoughtcrime.securesms.recipients.RecipientExporter; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ThemeUtil; @@ -27,8 +30,14 @@ import org.thoughtcrime.securesms.util.Util; import java.util.Objects; +/** + * A bottom sheet that shows some simple recipient details, as well as some actions (like calling, + * adding to contacts, etc). + */ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogFragment { + public static final int REQUEST_CODE_ADD_CONTACT = 1111; + private static final String ARGS_RECIPIENT_ID = "RECIPIENT_ID"; private static final String ARGS_GROUP_ID = "GROUP_ID"; @@ -40,6 +49,7 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF private Button secureCallButton; private Button blockButton; private Button unblockButton; + private Button addContactButton; private Button viewSafetyNumberButton; private Button makeGroupAdminButton; private Button removeAdminButton; @@ -67,6 +77,7 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF setStyle(DialogFragment.STYLE_NORMAL, ThemeUtil.isDarkTheme(requireContext()) ? R.style.Theme_Signal_RecipientBottomSheet : R.style.Theme_Signal_RecipientBottomSheet_Light); + super.onCreate(savedInstanceState); } @@ -74,18 +85,19 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.recipient_bottom_sheet, container, false); - avatar = view.findViewById(R.id.recipient_avatar); - fullName = view.findViewById(R.id.full_name); - usernameNumber = view.findViewById(R.id.username_number); - messageButton = view.findViewById(R.id.message_button); - secureCallButton = view.findViewById(R.id.secure_call_button); - blockButton = view.findViewById(R.id.block_button); - unblockButton = view.findViewById(R.id.unblock_button); - viewSafetyNumberButton = view.findViewById(R.id.view_safety_number_button); - makeGroupAdminButton = view.findViewById(R.id.make_group_admin_button); - removeAdminButton = view.findViewById(R.id.remove_group_admin_button); - removeFromGroupButton = view.findViewById(R.id.remove_from_group_button); - adminActionBusy = view.findViewById(R.id.admin_action_busy); + avatar = view.findViewById(R.id.rbs_recipient_avatar); + fullName = view.findViewById(R.id.rbs_full_name); + usernameNumber = view.findViewById(R.id.rbs_username_number); + messageButton = view.findViewById(R.id.rbs_message_button); + secureCallButton = view.findViewById(R.id.rbs_secure_call_button); + blockButton = view.findViewById(R.id.rbs_block_button); + unblockButton = view.findViewById(R.id.rbs_unblock_button); + addContactButton = view.findViewById(R.id.rbs_add_contact_button); + viewSafetyNumberButton = view.findViewById(R.id.rbs_view_safety_number_button); + makeGroupAdminButton = view.findViewById(R.id.rbs_make_group_admin_button); + removeAdminButton = view.findViewById(R.id.rbs_remove_group_admin_button); + removeFromGroupButton = view.findViewById(R.id.rbs_remove_from_group_button); + adminActionBusy = view.findViewById(R.id.rbs_admin_action_busy); return view; } @@ -125,6 +137,15 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF unblockButton.setVisibility(blocked ? View.VISIBLE : View.GONE); secureCallButton.setVisibility(recipient.isRegistered() ? View.VISIBLE : View.GONE); + + if (recipient.isSystemContact() || recipient.isGroup()) { + addContactButton.setVisibility(View.GONE); + } else { + addContactButton.setVisibility(View.VISIBLE); + addContactButton.setOnClickListener(v -> { + startActivityForResult(RecipientExporter.export(recipient).asAddContactIntent(), REQUEST_CODE_ADD_CONTACT); + }); + } }); viewModel.getAdminActionStatus().observe(getViewLifecycleOwner(), adminStatus -> { @@ -175,4 +196,11 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF removeFromGroupButton.setEnabled(!busy); }); } + + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_ADD_CONTACT) { + viewModel.onAddedToContacts(); + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogRepository.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogRepository.java index 6a6a40fd5c..2e494bb105 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogRepository.java @@ -6,6 +6,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.util.Consumer; +import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.groups.GroupChangeBusyException; @@ -64,6 +65,16 @@ final class RecipientDialogRepository { recipientCallback::onRecipient); } + void refreshRecipient() { + SignalExecutors.UNBOUNDED.execute(() -> { + try { + DirectoryHelper.refreshDirectoryFor(context, Recipient.resolved(recipientId), false); + } catch (IOException e) { + Log.w(TAG, "Failed to refresh user after adding to contacts."); + } + }); + } + void getGroupName(@NonNull Consumer stringConsumer) { SimpleTask.run(SignalExecutors.BOUNDED, () -> DatabaseFactory.getGroupDatabase(context).requireGroup(Objects.requireNonNull(groupId)).getTitle(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java index 538cfc6ac8..c32fca8e5a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java @@ -11,6 +11,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Transformations; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; @@ -166,6 +167,10 @@ final class RecipientDialogViewModel extends ViewModel { .show()); } + void onAddedToContacts() { + recipientDialogRepository.refreshRecipient(); + } + @WorkerThread private void showErrorToast(@NonNull GroupChangeFailureReason e) { Util.runOnMain(() -> Toast.makeText(context, GroupErrors.getUserDisplayMessage(e), Toast.LENGTH_LONG).show()); diff --git a/app/src/main/res/layout/recipient_bottom_sheet.xml b/app/src/main/res/layout/recipient_bottom_sheet.xml index 48d40c33a1..97a58534d8 100644 --- a/app/src/main/res/layout/recipient_bottom_sheet.xml +++ b/app/src/main/res/layout/recipient_bottom_sheet.xml @@ -7,7 +7,7 @@ tools:theme="@style/Theme.Signal.RecipientBottomSheet.Light"> + app:layout_constraintTop_toBottomOf="@id/rbs_recipient_avatar" + tools:text="Gwen Stacy" /> + app:layout_constraintTop_toBottomOf="@+id/rbs_full_name" + tools:text="\@spidergwen +1 555-654-6657" /> + app:layout_constraintTop_toBottomOf="@+id/rbs_username_number">