Convert EditProfileFragment to kotlin.

This commit is contained in:
Greyson Parrelli 2023-11-03 10:40:13 -04:00
parent 3a20375567
commit d00f2aa8d0
4 changed files with 300 additions and 326 deletions

View file

@ -1,322 +0,0 @@
package org.thoughtcrime.securesms.profiles.manage;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.res.ResourcesCompat;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.Navigation;
import com.airbnb.lottie.SimpleColorFilter;
import com.bumptech.glide.Glide;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.AvatarPreviewActivity;
import org.thoughtcrime.securesms.LoggingFragment;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.avatar.Avatars;
import org.thoughtcrime.securesms.avatar.picker.AvatarPickerFragment;
import org.thoughtcrime.securesms.badges.models.Badge;
import org.thoughtcrime.securesms.badges.self.none.BecomeASustainerFragment;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
import org.thoughtcrime.securesms.databinding.EditProfileFragmentBinding;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.profiles.manage.ManageProfileViewModel.AvatarState;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.signal.core.util.concurrent.LifecycleDisposable;
import org.thoughtcrime.securesms.util.NameUtil;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
import java.util.Arrays;
import java.util.Optional;
import io.reactivex.rxjava3.disposables.Disposable;
/**
* Fragment for editing your profile after you're already registered.
*/
public class EditProfileFragment extends LoggingFragment {
private static final String TAG = Log.tag(EditProfileFragment.class);
private AlertDialog avatarProgress;
private ManageProfileViewModel viewModel;
private EditProfileFragmentBinding binding;
private LifecycleDisposable disposables;
@Override
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = EditProfileFragmentBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
disposables = new LifecycleDisposable();
disposables.bindTo(getViewLifecycleOwner());
new UsernameEditFragment.ResultContract().registerForResult(getParentFragmentManager(), getViewLifecycleOwner(), isUsernameCreated -> {
Snackbar.make(view, R.string.ManageProfileFragment__username_created, Snackbar.LENGTH_SHORT).show();
});
UsernameShareBottomSheet.ResultContract.INSTANCE.registerForResult(getParentFragmentManager(), getViewLifecycleOwner(), isCopiedToClipboard -> {
Snackbar.make(view, R.string.ManageProfileFragment__username_copied, Snackbar.LENGTH_SHORT).show();
});
initializeViewModel();
binding.toolbar.setNavigationOnClickListener(v -> requireActivity().finish());
binding.manageProfileEditPhoto.setOnClickListener(v -> onEditAvatarClicked());
binding.manageProfileNameContainer.setOnClickListener(v -> {
SafeNavigation.safeNavigate(Navigation.findNavController(v), EditProfileFragmentDirections.actionManageProfileName());
});
binding.manageProfileUsernameContainer.setOnClickListener(v -> {
if (SignalStore.uiHints().hasSeenUsernameEducation()) {
if (Recipient.self().getUsername().isPresent()) {
new MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Signal_MaterialAlertDialog_List)
.setItems(R.array.username_edit_entries, (d, w) -> {
switch (w) {
case 0:
SafeNavigation.safeNavigate(Navigation.findNavController(v), EditProfileFragmentDirections.actionManageUsername());
break;
case 1:
displayConfirmUsernameDeletionDialog();
break;
default:
throw new IllegalStateException();
}
})
.show();
} else {
SafeNavigation.safeNavigate(Navigation.findNavController(v), EditProfileFragmentDirections.actionManageUsername());
}
} else {
SafeNavigation.safeNavigate(Navigation.findNavController(v), EditProfileFragmentDirections.actionManageProfileFragmentToUsernameEducationFragment());
}
});
binding.manageProfileAboutContainer.setOnClickListener(v -> {
SafeNavigation.safeNavigate(Navigation.findNavController(v), EditProfileFragmentDirections.actionManageAbout());
});
getParentFragmentManager().setFragmentResultListener(AvatarPickerFragment.REQUEST_KEY_SELECT_AVATAR, getViewLifecycleOwner(), (key, bundle) -> {
if (bundle.getBoolean(AvatarPickerFragment.SELECT_AVATAR_CLEAR)) {
viewModel.onAvatarSelected(requireContext(), null);
} else {
Media result = bundle.getParcelable(AvatarPickerFragment.SELECT_AVATAR_MEDIA);
viewModel.onAvatarSelected(requireContext(), result);
}
});
EmojiTextView avatarInitials = binding.manageProfileAvatarInitials;
avatarInitials.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
if (avatarInitials.length() > 0) {
updateInitials(avatarInitials.getText().toString());
}
});
binding.manageProfileBadgesContainer.setOnClickListener(v -> {
if (Recipient.self().getBadges().isEmpty()) {
BecomeASustainerFragment.show(getParentFragmentManager());
} else {
SafeNavigation.safeNavigate(Navigation.findNavController(v), EditProfileFragmentDirections.actionManageProfileFragmentToBadgeManageFragment());
}
});
binding.manageProfileAvatar.setOnClickListener(v -> {
startActivity(AvatarPreviewActivity.intentFromRecipientId(requireContext(), Recipient.self().getId()),
AvatarPreviewActivity.createTransitionBundle(requireActivity(), binding.manageProfileAvatar));
});
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
private void initializeViewModel() {
viewModel = new ViewModelProvider(this, new ManageProfileViewModel.Factory()).get(ManageProfileViewModel.class);
LiveData<Optional<byte[]>> avatarImage = Transformations.map(LiveDataUtil.distinctUntilChanged(viewModel.getAvatar(), (b1, b2) -> Arrays.equals(b1.getAvatar(), b2.getAvatar())),
b -> Optional.ofNullable(b.getAvatar()));
avatarImage.observe(getViewLifecycleOwner(), this::presentAvatarImage);
viewModel.getAvatar().observe(getViewLifecycleOwner(), this::presentAvatarPlaceholder);
viewModel.getProfileName().observe(getViewLifecycleOwner(), this::presentProfileName);
viewModel.getEvents().observe(getViewLifecycleOwner(), this::presentEvent);
viewModel.getAbout().observe(getViewLifecycleOwner(), this::presentAbout);
viewModel.getAboutEmoji().observe(getViewLifecycleOwner(), this::presentAboutEmoji);
viewModel.getBadge().observe(getViewLifecycleOwner(), this::presentBadge);
if (viewModel.shouldShowUsername()) {
viewModel.getUsername().observe(getViewLifecycleOwner(), this::presentUsername);
} else {
binding.manageProfileUsernameContainer.setVisibility(View.GONE);
}
}
private void presentAvatarImage(@NonNull Optional<byte[]> avatarData) {
if (avatarData.isPresent()) {
Glide.with(this)
.load(avatarData.get())
.circleCrop()
.into(binding.manageProfileAvatar);
} else {
Glide.with(this).load((Drawable) null).into(binding.manageProfileAvatar);
}
}
private void presentAvatarPlaceholder(@NonNull AvatarState avatarState) {
if (avatarState.getAvatar() == null) {
CharSequence initials = NameUtil.getAbbreviation(avatarState.getSelf().getDisplayName(requireContext()));
Avatars.ForegroundColor foregroundColor = Avatars.getForegroundColor(avatarState.getSelf().getAvatarColor());
binding.manageProfileAvatarBackground.setColorFilter(new SimpleColorFilter(avatarState.getSelf().getAvatarColor().colorInt()));
binding.manageProfileAvatarPlaceholder.setColorFilter(new SimpleColorFilter(foregroundColor.getColorInt()));
binding.manageProfileAvatarInitials.setTextColor(foregroundColor.getColorInt());
if (TextUtils.isEmpty(initials)) {
binding.manageProfileAvatarPlaceholder.setVisibility(View.VISIBLE);
binding.manageProfileAvatarInitials.setVisibility(View.GONE);
} else {
updateInitials(initials.toString());
binding.manageProfileAvatarPlaceholder.setVisibility(View.GONE);
binding.manageProfileAvatarInitials.setVisibility(View.VISIBLE);
}
} else {
binding.manageProfileAvatarPlaceholder.setVisibility(View.GONE);
binding.manageProfileAvatarInitials.setVisibility(View.GONE);
}
if (avatarProgress == null && avatarState.getLoadingState() == ManageProfileViewModel.LoadingState.LOADING) {
avatarProgress = SimpleProgressDialog.show(requireContext());
} else if (avatarProgress != null && avatarState.getLoadingState() == ManageProfileViewModel.LoadingState.LOADED) {
avatarProgress.dismiss();
}
}
private void updateInitials(String initials) {
binding.manageProfileAvatarInitials.setTextSize(TypedValue.COMPLEX_UNIT_PX,
Avatars.getTextSizeForLength(requireContext(),
initials,
binding.manageProfileAvatarInitials.getMeasuredWidth() * 0.8f,
binding.manageProfileAvatarInitials.getMeasuredWidth() * 0.45f));
binding.manageProfileAvatarInitials.setText(initials);
}
private void presentProfileName(@Nullable ProfileName profileName) {
if (profileName == null || profileName.isEmpty()) {
binding.manageProfileName.setText(R.string.ManageProfileFragment_profile_name);
} else {
binding.manageProfileName.setText(profileName.toString());
}
}
private void presentUsername(@Nullable String username) {
if (username == null || username.isEmpty()) {
binding.manageProfileUsername.setText(R.string.ManageProfileFragment_username);
} else {
binding.manageProfileUsername.setText(username);
}
}
private void presentAbout(@Nullable String about) {
if (about == null || about.isEmpty()) {
binding.manageProfileAbout.setText(R.string.ManageProfileFragment_about);
} else {
binding.manageProfileAbout.setText(about);
}
}
private void presentAboutEmoji(@NonNull String aboutEmoji) {
if (aboutEmoji == null || aboutEmoji.isEmpty()) {
binding.manageProfileAboutIcon.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.symbol_edit_24, null));
} else {
Drawable emoji = EmojiUtil.convertToDrawable(requireContext(), aboutEmoji);
if (emoji != null) {
binding.manageProfileAboutIcon.setImageDrawable(emoji);
} else {
binding.manageProfileAboutIcon.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.symbol_edit_24, null));
}
}
}
private void presentBadge(@NonNull Optional<Badge> badge) {
if (badge.isPresent() && badge.get().getVisible() && !badge.get().isExpired()) {
binding.manageProfileBadge.setBadge(badge.orElse(null));
} else {
binding.manageProfileBadge.setBadge(null);
}
}
private void presentEvent(@NonNull ManageProfileViewModel.Event event) {
switch (event) {
case AVATAR_DISK_FAILURE:
Toast.makeText(requireContext(), R.string.ManageProfileFragment_failed_to_set_avatar, Toast.LENGTH_LONG).show();
break;
case AVATAR_NETWORK_FAILURE:
Toast.makeText(requireContext(), R.string.EditProfileNameFragment_failed_to_save_due_to_network_issues_try_again_later, Toast.LENGTH_LONG).show();
break;
}
}
private void onEditAvatarClicked() {
SafeNavigation.safeNavigate(Navigation.findNavController(requireView()), EditProfileFragmentDirections.actionManageProfileFragmentToAvatarPicker(null, null));
}
private void displayConfirmUsernameDeletionDialog() {
new MaterialAlertDialogBuilder(requireContext())
.setTitle("Delete Username?") // TODO [alex] -- Final copy
.setMessage("This will remove your username, allowing other users to claim it. Are you sure?") // TODO [alex] -- Final copy
.setPositiveButton(R.string.delete, (d, w) -> {
onUserConfirmedUsernameDeletion();
})
.setNegativeButton(android.R.string.cancel, (d, w) -> {})
.show();
}
private void onUserConfirmedUsernameDeletion() {
binding.progressCard.setVisibility(View.VISIBLE);
Disposable disposable = viewModel.deleteUsername()
.subscribe(result -> {
binding.progressCard.setVisibility(View.GONE);
handleUsernameDeletionResult(result);
});
disposables.add(disposable);
}
private void handleUsernameDeletionResult(@NonNull UsernameRepository.UsernameDeleteResult usernameDeleteResult) {
switch (usernameDeleteResult) {
case SUCCESS:
Snackbar.make(requireView(), R.string.ManageProfileFragment__username_deleted, Snackbar.LENGTH_SHORT).show();
break;
case NETWORK_ERROR:
Snackbar.make(requireView(), R.string.ManageProfileFragment__couldnt_delete_username, Snackbar.LENGTH_SHORT).show();
break;
}
}
}

View file

@ -0,0 +1,291 @@
package org.thoughtcrime.securesms.profiles.manage
import android.content.DialogInterface
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.text.TextUtils
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.map
import androidx.navigation.Navigation.findNavController
import com.airbnb.lottie.SimpleColorFilter
import com.bumptech.glide.Glide
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import org.signal.core.util.concurrent.LifecycleDisposable
import org.thoughtcrime.securesms.AvatarPreviewActivity
import org.thoughtcrime.securesms.LoggingFragment
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.avatar.Avatars.getForegroundColor
import org.thoughtcrime.securesms.avatar.Avatars.getTextSizeForLength
import org.thoughtcrime.securesms.avatar.picker.AvatarPickerFragment
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.badges.self.none.BecomeASustainerFragment.Companion.show
import org.thoughtcrime.securesms.components.emoji.EmojiUtil
import org.thoughtcrime.securesms.databinding.EditProfileFragmentBinding
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.profiles.ProfileName
import org.thoughtcrime.securesms.profiles.manage.ManageProfileViewModel.AvatarState
import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.UsernameDeleteResult
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.NameUtil.getAbbreviation
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
import org.thoughtcrime.securesms.util.navigation.safeNavigate
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog
import java.util.Arrays
import java.util.Optional
/**
* Fragment for editing your profile after you're already registered.
*/
class EditProfileFragment : LoggingFragment() {
private var avatarProgress: AlertDialog? = null
private lateinit var viewModel: ManageProfileViewModel
private lateinit var binding: EditProfileFragmentBinding
private lateinit var disposables: LifecycleDisposable
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = EditProfileFragmentBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
disposables = LifecycleDisposable()
disposables.bindTo(viewLifecycleOwner)
UsernameEditFragment.ResultContract().registerForResult(parentFragmentManager, viewLifecycleOwner) {
Snackbar.make(view, R.string.ManageProfileFragment__username_created, Snackbar.LENGTH_SHORT).show()
}
UsernameShareBottomSheet.ResultContract.registerForResult(parentFragmentManager, viewLifecycleOwner) {
Snackbar.make(view, R.string.ManageProfileFragment__username_copied, Snackbar.LENGTH_SHORT).show()
}
initializeViewModel()
binding.toolbar.setNavigationOnClickListener { requireActivity().finish() }
binding.manageProfileEditPhoto.setOnClickListener { onEditAvatarClicked() }
binding.manageProfileNameContainer.setOnClickListener { v: View -> findNavController(v).safeNavigate(EditProfileFragmentDirections.actionManageProfileName()) }
binding.manageProfileUsernameContainer.setOnClickListener { v: View ->
if (SignalStore.uiHints().hasSeenUsernameEducation()) {
if (SignalStore.account().username != null) {
MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Signal_MaterialAlertDialog_List)
.setItems(R.array.username_edit_entries) { _: DialogInterface?, w: Int ->
when (w) {
0 -> findNavController(v).safeNavigate(EditProfileFragmentDirections.actionManageUsername())
1 -> displayConfirmUsernameDeletionDialog()
else -> throw IllegalStateException()
}
}
.show()
} else {
findNavController(v).safeNavigate(EditProfileFragmentDirections.actionManageUsername())
}
} else {
findNavController(v).safeNavigate(EditProfileFragmentDirections.actionManageProfileFragmentToUsernameEducationFragment())
}
}
binding.manageProfileAboutContainer.setOnClickListener { v: View -> findNavController(v).safeNavigate(EditProfileFragmentDirections.actionManageAbout()) }
parentFragmentManager.setFragmentResultListener(AvatarPickerFragment.REQUEST_KEY_SELECT_AVATAR, viewLifecycleOwner) { _: String?, bundle: Bundle ->
if (bundle.getBoolean(AvatarPickerFragment.SELECT_AVATAR_CLEAR)) {
viewModel.onAvatarSelected(requireContext(), null)
} else {
val result = bundle.getParcelable<Media>(AvatarPickerFragment.SELECT_AVATAR_MEDIA)
viewModel.onAvatarSelected(requireContext(), result)
}
}
val avatarInitials = binding.manageProfileAvatarInitials
avatarInitials.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
if (avatarInitials.length() > 0) {
updateInitials(avatarInitials.text.toString())
}
}
binding.manageProfileBadgesContainer.setOnClickListener { v: View ->
if (Recipient.self().badges.isEmpty()) {
show(parentFragmentManager)
} else {
findNavController(v).safeNavigate(EditProfileFragmentDirections.actionManageProfileFragmentToBadgeManageFragment())
}
}
binding.manageProfileAvatar.setOnClickListener {
startActivity(
AvatarPreviewActivity.intentFromRecipientId(requireContext(), Recipient.self().id),
AvatarPreviewActivity.createTransitionBundle(requireActivity(), binding.manageProfileAvatar)
)
}
}
private fun initializeViewModel() {
viewModel = ViewModelProvider(this, ManageProfileViewModel.Factory()).get(ManageProfileViewModel::class.java)
LiveDataUtil
.distinctUntilChanged(viewModel.avatar) { b1, b2 -> Arrays.equals(b1.avatar, b2.avatar) }
.map { avatarState -> Optional.ofNullable(avatarState.avatar) }
.observe(viewLifecycleOwner) { avatarData -> presentAvatarImage(avatarData) }
viewModel.avatar.observe(viewLifecycleOwner) { presentAvatarPlaceholder(it) }
viewModel.profileName.observe(viewLifecycleOwner) { presentProfileName(it) }
viewModel.events.observe(viewLifecycleOwner) { presentEvent(it) }
viewModel.about.observe(viewLifecycleOwner) { presentAbout(it) }
viewModel.aboutEmoji.observe(viewLifecycleOwner) { presentAboutEmoji(it) }
viewModel.badge.observe(viewLifecycleOwner) { presentBadge(it) }
if (viewModel.shouldShowUsername()) {
viewModel.username.observe(viewLifecycleOwner) { presentUsername(it) }
} else {
binding.manageProfileUsernameContainer.visibility = View.GONE
}
}
private fun presentAvatarImage(avatarData: Optional<ByteArray>) {
if (avatarData.isPresent) {
Glide.with(this)
.load(avatarData.get())
.circleCrop()
.into(binding.manageProfileAvatar)
} else {
Glide.with(this).load(null as Drawable?).into(binding.manageProfileAvatar)
}
}
private fun presentAvatarPlaceholder(avatarState: AvatarState) {
if (avatarState.avatar == null) {
val initials: CharSequence? = getAbbreviation(avatarState.self.getDisplayName(requireContext()))
val foregroundColor = getForegroundColor(avatarState.self.avatarColor)
binding.manageProfileAvatarBackground.colorFilter = SimpleColorFilter(avatarState.self.avatarColor.colorInt())
binding.manageProfileAvatarPlaceholder.colorFilter = SimpleColorFilter(foregroundColor.colorInt)
binding.manageProfileAvatarInitials.setTextColor(foregroundColor.colorInt)
if (TextUtils.isEmpty(initials)) {
binding.manageProfileAvatarPlaceholder.visibility = View.VISIBLE
binding.manageProfileAvatarInitials.visibility = View.GONE
} else {
updateInitials(initials.toString())
binding.manageProfileAvatarPlaceholder.visibility = View.GONE
binding.manageProfileAvatarInitials.visibility = View.VISIBLE
}
} else {
binding.manageProfileAvatarPlaceholder.visibility = View.GONE
binding.manageProfileAvatarInitials.visibility = View.GONE
}
if (avatarProgress == null && avatarState.loadingState == ManageProfileViewModel.LoadingState.LOADING) {
avatarProgress = SimpleProgressDialog.show(requireContext())
} else if (avatarProgress != null && avatarState.loadingState == ManageProfileViewModel.LoadingState.LOADED) {
avatarProgress!!.dismiss()
}
}
private fun updateInitials(initials: String) {
binding.manageProfileAvatarInitials.setTextSize(
TypedValue.COMPLEX_UNIT_PX,
getTextSizeForLength(
context = requireContext(),
text = initials,
maxWidth = binding.manageProfileAvatarInitials.measuredWidth * 0.8f,
maxSize = binding.manageProfileAvatarInitials.measuredWidth * 0.45f
)
)
binding.manageProfileAvatarInitials.text = initials
}
private fun presentProfileName(profileName: ProfileName?) {
if (profileName == null || profileName.isEmpty) {
binding.manageProfileName.setText(R.string.ManageProfileFragment_profile_name)
} else {
binding.manageProfileName.text = profileName.toString()
}
}
private fun presentUsername(username: String?) {
if (username.isNullOrEmpty()) {
binding.manageProfileUsername.setText(R.string.ManageProfileFragment_username)
} else {
binding.manageProfileUsername.text = username
}
}
private fun presentAbout(about: String?) {
if (about.isNullOrEmpty()) {
binding.manageProfileAbout.setText(R.string.ManageProfileFragment_about)
} else {
binding.manageProfileAbout.text = about
}
}
private fun presentAboutEmoji(aboutEmoji: String?) {
if (aboutEmoji.isNullOrEmpty()) {
binding.manageProfileAboutIcon.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.symbol_edit_24, null))
} else {
val emoji = EmojiUtil.convertToDrawable(requireContext(), aboutEmoji)
if (emoji != null) {
binding.manageProfileAboutIcon.setImageDrawable(emoji)
} else {
binding.manageProfileAboutIcon.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.symbol_edit_24, null))
}
}
}
private fun presentBadge(badge: Optional<Badge>) {
if (badge.isPresent && badge.get().visible && !badge.get().isExpired()) {
binding.manageProfileBadge.setBadge(badge.orElse(null))
} else {
binding.manageProfileBadge.setBadge(null)
}
}
private fun presentEvent(event: ManageProfileViewModel.Event) {
when (event) {
ManageProfileViewModel.Event.AVATAR_DISK_FAILURE -> Toast.makeText(requireContext(), R.string.ManageProfileFragment_failed_to_set_avatar, Toast.LENGTH_LONG).show()
ManageProfileViewModel.Event.AVATAR_NETWORK_FAILURE -> Toast.makeText(requireContext(), R.string.EditProfileNameFragment_failed_to_save_due_to_network_issues_try_again_later, Toast.LENGTH_LONG).show()
}
}
private fun onEditAvatarClicked() {
findNavController(requireView()).safeNavigate(EditProfileFragmentDirections.actionManageProfileFragmentToAvatarPicker(null, null))
}
private fun displayConfirmUsernameDeletionDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.ManageProfileFragment__delete_username_dialog_title)
.setMessage(requireContext().getString(R.string.ManageProfileFragment__delete_username_dialog_body, SignalStore.account().username))
.setPositiveButton(R.string.delete) { _, _ -> onUserConfirmedUsernameDeletion() }
.setNegativeButton(android.R.string.cancel) { d: DialogInterface?, w: Int -> }
.show()
}
private fun onUserConfirmedUsernameDeletion() {
binding.progressCard.visibility = View.VISIBLE
disposables += viewModel
.deleteUsername()
.subscribe { result: UsernameDeleteResult ->
binding.progressCard.visibility = View.GONE
handleUsernameDeletionResult(result)
}
}
private fun handleUsernameDeletionResult(usernameDeleteResult: UsernameDeleteResult) {
when (usernameDeleteResult) {
UsernameDeleteResult.SUCCESS -> Snackbar.make(requireView(), R.string.ManageProfileFragment__username_deleted, Snackbar.LENGTH_SHORT).show()
UsernameDeleteResult.NETWORK_ERROR -> Snackbar.make(requireView(), R.string.ManageProfileFragment__couldnt_delete_username, Snackbar.LENGTH_SHORT).show()
}
}
}

View file

@ -14,7 +14,6 @@ import org.signal.core.util.Result
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.UsernameDeleteResult
import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.UsernameSetResult
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.UsernameUtil.InvalidReason
import org.thoughtcrime.securesms.util.UsernameUtil.checkUsername
import org.thoughtcrime.securesms.util.rx.RxStore
@ -41,7 +40,7 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr
defaultValue = State(
buttonState = ButtonState.SUBMIT_DISABLED,
usernameStatus = UsernameStatus.NONE,
username = Recipient.self().username.map<UsernameState> { UsernameState.Set(it) }.orElse(UsernameState.NoUsername)
username = SignalStore.account().username?.let { UsernameState.Set(it) } ?: UsernameState.NoUsername
),
scheduler = Schedulers.computation()
)
@ -60,7 +59,7 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr
fun onNicknameUpdated(nickname: String) {
uiState.update { state: State ->
if (nickname.isBlank() && Recipient.self().username.isPresent) {
if (nickname.isBlank() && SignalStore.account().username != null) {
return@update State(
buttonState = if (isInRegistration) ButtonState.SUBMIT_DISABLED else ButtonState.DELETE,
usernameStatus = UsernameStatus.NONE,
@ -102,7 +101,7 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr
return
}
if (usernameState.username == Recipient.self().username.orElse(null)) {
if (usernameState.username == SignalStore.account().username) {
uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, it.username) }
return
}

View file

@ -993,6 +993,8 @@
<string name="ManageProfileFragment_about">About</string>
<string name="ManageProfileFragment_failed_to_set_avatar">Failed to set avatar</string>
<string name="ManageProfileFragment_badges">Badges</string>
<!-- Text for a button that will take the user to the screen to manage their username link and QR code -->
<string name="ManageProfileFragment_link_setting_text">QR code or link</string>
<string name="ManageProfileFragment__edit_photo">Edit photo</string>
<!-- Snackbar message after creating username -->
<string name="ManageProfileFragment__username_created">Username created</string>
@ -1002,6 +1004,10 @@
<string name="ManageProfileFragment__couldnt_delete_username">Couldn\'t delete username. Try again later.</string>
<!-- Snackbar message after successful deletion of username -->
<string name="ManageProfileFragment__username_deleted">Username deleted</string>
<!-- The title of a pop-up dialog asking the user to confirm deleting their username -->
<string name="ManageProfileFragment__delete_username_dialog_title">Delete username?</string>
<!-- The body of a pop-up dialog asking the user to confirm deleting their username -->
<string name="ManageProfileFragment__delete_username_dialog_body">This will remove your username and disable your QR code and link. "%1$s" will be available for others to claim. Are you sure?</string>
<!-- UsernameOutOfSyncReminder -->
<!-- Displayed above the conversation list when a user needs to address an issue with their username -->