kopia lustrzana https://github.com/ryukoposting/Signal-Android
385 wiersze
17 KiB
Java
385 wiersze
17 KiB
Java
package org.thoughtcrime.securesms.recipients.ui.bottomsheet;
|
|
|
|
import android.app.Activity;
|
|
import android.content.ActivityNotFoundException;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Bundle;
|
|
import android.text.SpannableStringBuilder;
|
|
import android.text.TextUtils;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.ProgressBar;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.core.content.ContextCompat;
|
|
import androidx.fragment.app.DialogFragment;
|
|
import androidx.fragment.app.FragmentManager;
|
|
import androidx.lifecycle.ViewModelProvider;
|
|
|
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
|
|
|
import org.signal.core.util.logging.Log;
|
|
import org.thoughtcrime.securesms.R;
|
|
import org.thoughtcrime.securesms.avatar.view.AvatarView;
|
|
import org.thoughtcrime.securesms.badges.BadgeImageView;
|
|
import org.thoughtcrime.securesms.badges.view.ViewBadgeBottomSheetDialogFragment;
|
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsIcon;
|
|
import org.thoughtcrime.securesms.components.settings.conversation.preferences.ButtonStripPreference;
|
|
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
|
import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto80dp;
|
|
import org.thoughtcrime.securesms.groups.GroupId;
|
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
import org.thoughtcrime.securesms.recipients.RecipientExporter;
|
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
|
import org.thoughtcrime.securesms.util.BottomSheetUtil;
|
|
import org.thoughtcrime.securesms.util.ContextUtil;
|
|
import org.thoughtcrime.securesms.util.DrawableUtil;
|
|
import org.thoughtcrime.securesms.util.ServiceUtil;
|
|
import org.thoughtcrime.securesms.util.SpanUtil;
|
|
import org.thoughtcrime.securesms.util.ThemeUtil;
|
|
import org.thoughtcrime.securesms.util.Util;
|
|
|
|
import java.util.Objects;
|
|
|
|
import kotlin.Unit;
|
|
|
|
/**
|
|
* 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 String TAG = Log.tag(RecipientBottomSheetDialogFragment.class);
|
|
|
|
public static final int REQUEST_CODE_SYSTEM_CONTACT_SHEET = 1111;
|
|
|
|
private static final String ARGS_RECIPIENT_ID = "RECIPIENT_ID";
|
|
private static final String ARGS_GROUP_ID = "GROUP_ID";
|
|
|
|
private RecipientDialogViewModel viewModel;
|
|
private AvatarView avatar;
|
|
private TextView fullName;
|
|
private TextView about;
|
|
private TextView usernameNumber;
|
|
private TextView blockButton;
|
|
private TextView unblockButton;
|
|
private TextView addContactButton;
|
|
private TextView contactDetailsButton;
|
|
private TextView addToGroupButton;
|
|
private TextView viewSafetyNumberButton;
|
|
private TextView makeGroupAdminButton;
|
|
private TextView removeAdminButton;
|
|
private TextView removeFromGroupButton;
|
|
private ProgressBar adminActionBusy;
|
|
private View noteToSelfDescription;
|
|
private View buttonStrip;
|
|
private View interactionsContainer;
|
|
private BadgeImageView badgeImageView;
|
|
private Callback callback;
|
|
|
|
public static BottomSheetDialogFragment create(@NonNull RecipientId recipientId,
|
|
@Nullable GroupId groupId)
|
|
{
|
|
Bundle args = new Bundle();
|
|
RecipientBottomSheetDialogFragment fragment = new RecipientBottomSheetDialogFragment();
|
|
|
|
args.putString(ARGS_RECIPIENT_ID, recipientId.serialize());
|
|
if (groupId != null) {
|
|
args.putString(ARGS_GROUP_ID, groupId.toString());
|
|
}
|
|
|
|
fragment.setArguments(args);
|
|
|
|
return fragment;
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
setStyle(DialogFragment.STYLE_NORMAL,
|
|
ThemeUtil.isDarkTheme(requireContext()) ? R.style.Theme_Signal_RoundedBottomSheet
|
|
: R.style.Theme_Signal_RoundedBottomSheet_Light);
|
|
|
|
super.onCreate(savedInstanceState);
|
|
}
|
|
|
|
@Override
|
|
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.rbs_recipient_avatar);
|
|
fullName = view.findViewById(R.id.rbs_full_name);
|
|
about = view.findViewById(R.id.rbs_about);
|
|
usernameNumber = view.findViewById(R.id.rbs_username_number);
|
|
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);
|
|
contactDetailsButton = view.findViewById(R.id.rbs_contact_details_button);
|
|
addToGroupButton = view.findViewById(R.id.rbs_add_to_group_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);
|
|
noteToSelfDescription = view.findViewById(R.id.rbs_note_to_self_description);
|
|
buttonStrip = view.findViewById(R.id.button_strip);
|
|
interactionsContainer = view.findViewById(R.id.interactions_container);
|
|
badgeImageView = view.findViewById(R.id.rbs_badge);
|
|
|
|
return view;
|
|
}
|
|
|
|
@Override
|
|
public void onViewCreated(@NonNull View fragmentView, @Nullable Bundle savedInstanceState) {
|
|
super.onViewCreated(fragmentView, savedInstanceState);
|
|
|
|
Bundle arguments = requireArguments();
|
|
RecipientId recipientId = RecipientId.from(Objects.requireNonNull(arguments.getString(ARGS_RECIPIENT_ID)));
|
|
GroupId groupId = GroupId.parseNullableOrThrow(arguments.getString(ARGS_GROUP_ID));
|
|
|
|
RecipientDialogViewModel.Factory factory = new RecipientDialogViewModel.Factory(requireContext().getApplicationContext(), recipientId, groupId);
|
|
|
|
viewModel = new ViewModelProvider(this, factory).get(RecipientDialogViewModel.class);
|
|
|
|
viewModel.getStoryViewState().observe(getViewLifecycleOwner(), state -> {
|
|
avatar.setStoryRingFromState(state);
|
|
});
|
|
|
|
viewModel.getRecipient().observe(getViewLifecycleOwner(), recipient -> {
|
|
interactionsContainer.setVisibility(recipient.isSelf() ? View.GONE : View.VISIBLE);
|
|
|
|
avatar.setFallbackPhotoProvider(new Recipient.FallbackPhotoProvider() {
|
|
@Override
|
|
public @NonNull FallbackContactPhoto getPhotoForLocalNumber() {
|
|
return new FallbackPhoto80dp(R.drawable.ic_note_80, recipient.getAvatarColor());
|
|
}
|
|
});
|
|
avatar.displayChatAvatar(recipient);
|
|
|
|
if (!recipient.isSelf()) {
|
|
badgeImageView.setBadgeFromRecipient(recipient);
|
|
}
|
|
|
|
if (recipient.isSelf()) {
|
|
avatar.setOnClickListener(v -> {
|
|
dismiss();
|
|
viewModel.onNoteToSelfClicked(requireActivity());
|
|
});
|
|
}
|
|
|
|
String name = recipient.isSelf() ? requireContext().getString(R.string.note_to_self)
|
|
: recipient.getDisplayName(requireContext());
|
|
fullName.setVisibility(TextUtils.isEmpty(name) ? View.GONE : View.VISIBLE);
|
|
SpannableStringBuilder nameBuilder = new SpannableStringBuilder(name);
|
|
if (recipient.isSystemContact() && !recipient.isSelf()) {
|
|
Drawable systemContact = DrawableUtil.tint(ContextUtil.requireDrawable(requireContext(), R.drawable.ic_profile_circle_outline_16),
|
|
ContextCompat.getColor(requireContext(), R.color.signal_text_primary));
|
|
SpanUtil.appendCenteredImageSpan(nameBuilder, systemContact, 16, 16);
|
|
} else if (recipient.showVerified()) {
|
|
SpanUtil.appendCenteredImageSpan(nameBuilder, ContextUtil.requireDrawable(requireContext(), R.drawable.ic_official_28), 28, 28);
|
|
}
|
|
fullName.setText(nameBuilder);
|
|
|
|
String aboutText = recipient.getCombinedAboutAndEmoji();
|
|
if (recipient.isReleaseNotes()) {
|
|
aboutText = getString(R.string.ReleaseNotes__signal_release_notes_and_news);
|
|
}
|
|
|
|
if (!Util.isEmpty(aboutText)) {
|
|
about.setText(aboutText);
|
|
about.setVisibility(View.VISIBLE);
|
|
} else {
|
|
about.setVisibility(View.GONE);
|
|
}
|
|
|
|
String usernameNumberString = recipient.hasAUserSetDisplayName(requireContext()) && !recipient.isSelf()
|
|
? recipient.getSmsAddress().map(PhoneNumberFormatter::prettyPrint).orElse("").trim()
|
|
: "";
|
|
usernameNumber.setText(usernameNumberString);
|
|
usernameNumber.setVisibility(TextUtils.isEmpty(usernameNumberString) ? View.GONE : View.VISIBLE);
|
|
usernameNumber.setOnLongClickListener(v -> {
|
|
Util.copyToClipboard(v.getContext(), usernameNumber.getText().toString());
|
|
ServiceUtil.getVibrator(v.getContext()).vibrate(250);
|
|
Toast.makeText(v.getContext(), R.string.RecipientBottomSheet_copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
|
return true;
|
|
});
|
|
|
|
noteToSelfDescription.setVisibility(recipient.isSelf() ? View.VISIBLE : View.GONE);
|
|
|
|
if (RecipientUtil.isBlockable(recipient)) {
|
|
boolean blocked = recipient.isBlocked();
|
|
|
|
blockButton .setVisibility(recipient.isSelf() || blocked ? View.GONE : View.VISIBLE);
|
|
unblockButton.setVisibility(recipient.isSelf() || !blocked ? View.GONE : View.VISIBLE);
|
|
} else {
|
|
blockButton .setVisibility(View.GONE);
|
|
unblockButton.setVisibility(View.GONE);
|
|
}
|
|
|
|
boolean isAudioAvailable = (recipient.isRegistered() || SignalStore.misc().getSmsExportPhase().allowSmsFeatures()) &&
|
|
!recipient.isGroup() &&
|
|
!recipient.isBlocked() &&
|
|
!recipient.isSelf() &&
|
|
!recipient.isReleaseNotes();
|
|
|
|
ButtonStripPreference.State buttonStripState = new ButtonStripPreference.State(
|
|
/* isMessageAvailable = */ !recipient.isBlocked() && !recipient.isSelf() && !recipient.isReleaseNotes(),
|
|
/* isVideoAvailable = */ !recipient.isBlocked() && !recipient.isSelf() && recipient.isRegistered(),
|
|
/* isAudioAvailable = */ isAudioAvailable,
|
|
/* isMuteAvailable = */ false,
|
|
/* isSearchAvailable = */ false,
|
|
/* isAudioSecure = */ recipient.isRegistered(),
|
|
/* isMuted = */ false,
|
|
/* isAddToStoryAvailable = */ false
|
|
);
|
|
|
|
ButtonStripPreference.Model buttonStripModel = new ButtonStripPreference.Model(
|
|
buttonStripState,
|
|
DSLSettingsIcon.from(ContextUtil.requireDrawable(requireContext(), R.drawable.selectable_recipient_bottom_sheet_icon_button)),
|
|
() -> Unit.INSTANCE,
|
|
() -> {
|
|
dismiss();
|
|
viewModel.onMessageClicked(requireActivity());
|
|
return Unit.INSTANCE;
|
|
},
|
|
() -> {
|
|
viewModel.onSecureVideoCallClicked(requireActivity());
|
|
return Unit.INSTANCE;
|
|
},
|
|
() -> {
|
|
if (buttonStripState.isAudioSecure()) {
|
|
viewModel.onSecureCallClicked(requireActivity());
|
|
} else {
|
|
viewModel.onInsecureCallClicked(requireActivity());
|
|
}
|
|
return Unit.INSTANCE;
|
|
},
|
|
() -> Unit.INSTANCE,
|
|
() -> Unit.INSTANCE
|
|
);
|
|
|
|
new ButtonStripPreference.ViewHolder(buttonStrip).bind(buttonStripModel);
|
|
|
|
if (recipient.isReleaseNotes()) {
|
|
buttonStrip.setVisibility(View.GONE);
|
|
}
|
|
|
|
if (recipient.isSystemContact() || recipient.isGroup() || recipient.isSelf() || recipient.isBlocked() || recipient.isReleaseNotes()) {
|
|
addContactButton.setVisibility(View.GONE);
|
|
} else {
|
|
addContactButton.setVisibility(View.VISIBLE);
|
|
addContactButton.setOnClickListener(v -> {
|
|
openSystemContactSheet(RecipientExporter.export(recipient).asAddContactIntent());
|
|
});
|
|
}
|
|
|
|
if (recipient.isSystemContact() && !recipient.isGroup() && !recipient.isSelf()) {
|
|
contactDetailsButton.setVisibility(View.VISIBLE);
|
|
contactDetailsButton.setOnClickListener(v -> {
|
|
openSystemContactSheet(new Intent(Intent.ACTION_VIEW, recipient.getContactUri()));
|
|
});
|
|
} else {
|
|
contactDetailsButton.setVisibility(View.GONE);
|
|
}
|
|
});
|
|
|
|
viewModel.getCanAddToAGroup().observe(getViewLifecycleOwner(), canAdd -> {
|
|
addToGroupButton.setText(groupId == null ? R.string.RecipientBottomSheet_add_to_a_group : R.string.RecipientBottomSheet_add_to_another_group);
|
|
addToGroupButton.setVisibility(canAdd ? View.VISIBLE : View.GONE);
|
|
});
|
|
|
|
viewModel.getAdminActionStatus().observe(getViewLifecycleOwner(), adminStatus -> {
|
|
makeGroupAdminButton.setVisibility(adminStatus.isCanMakeAdmin() ? View.VISIBLE : View.GONE);
|
|
removeAdminButton.setVisibility(adminStatus.isCanMakeNonAdmin() ? View.VISIBLE : View.GONE);
|
|
removeFromGroupButton.setVisibility(adminStatus.isCanRemove() ? View.VISIBLE : View.GONE);
|
|
|
|
if (adminStatus.isCanRemove()) {
|
|
removeFromGroupButton.setOnClickListener(view -> viewModel.onRemoveFromGroupClicked(requireActivity(), adminStatus.isLinkActive(), this::dismiss));
|
|
}
|
|
});
|
|
|
|
viewModel.getIdentity().observe(getViewLifecycleOwner(), identityRecord -> {
|
|
viewSafetyNumberButton.setVisibility(identityRecord != null ? View.VISIBLE : View.GONE);
|
|
|
|
if (identityRecord != null) {
|
|
viewSafetyNumberButton.setOnClickListener(view -> {
|
|
dismiss();
|
|
viewModel.onViewSafetyNumberClicked(requireActivity(), identityRecord);
|
|
});
|
|
}
|
|
});
|
|
|
|
avatar.setOnClickListener(view -> {
|
|
dismiss();
|
|
viewModel.onAvatarClicked(requireActivity());
|
|
});
|
|
|
|
badgeImageView.setOnClickListener(view -> {
|
|
dismiss();
|
|
ViewBadgeBottomSheetDialogFragment.show(getParentFragmentManager(), recipientId, null);
|
|
});
|
|
|
|
blockButton.setOnClickListener(view -> viewModel.onBlockClicked(requireActivity()));
|
|
unblockButton.setOnClickListener(view -> viewModel.onUnblockClicked(requireActivity()));
|
|
|
|
makeGroupAdminButton.setOnClickListener(view -> viewModel.onMakeGroupAdminClicked(requireActivity()));
|
|
removeAdminButton.setOnClickListener(view -> viewModel.onRemoveGroupAdminClicked(requireActivity()));
|
|
|
|
addToGroupButton.setOnClickListener(view -> {
|
|
dismiss();
|
|
viewModel.onAddToGroupButton(requireActivity());
|
|
});
|
|
|
|
viewModel.getAdminActionBusy().observe(getViewLifecycleOwner(), busy -> {
|
|
adminActionBusy.setVisibility(busy ? View.VISIBLE : View.GONE);
|
|
|
|
makeGroupAdminButton.setEnabled(!busy);
|
|
removeAdminButton.setEnabled(!busy);
|
|
removeFromGroupButton.setEnabled(!busy);
|
|
});
|
|
|
|
callback = getParentFragment() != null && getParentFragment() instanceof Callback ? (Callback) getParentFragment() : null;
|
|
}
|
|
|
|
private void openSystemContactSheet(@NonNull Intent intent) {
|
|
try {
|
|
startActivityForResult(intent, REQUEST_CODE_SYSTEM_CONTACT_SHEET);
|
|
} catch (ActivityNotFoundException e) {
|
|
Log.w(TAG, "No activity existed to open the contact.");
|
|
Toast.makeText(requireContext(), R.string.RecipientBottomSheet_unable_to_open_contacts, Toast.LENGTH_LONG).show();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
|
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_SYSTEM_CONTACT_SHEET) {
|
|
viewModel.refreshRecipient();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void show(@NonNull FragmentManager manager, @Nullable String tag) {
|
|
BottomSheetUtil.show(manager, tag, this);
|
|
}
|
|
|
|
@Override
|
|
public void onDismiss(@NonNull DialogInterface dialog) {
|
|
super.onDismiss(dialog);
|
|
if (callback != null) {
|
|
callback.onRecipientBottomSheetDismissed();
|
|
}
|
|
}
|
|
|
|
public interface Callback {
|
|
void onRecipientBottomSheetDismissed();
|
|
}
|
|
}
|