From d00f2aa8d0d5e84e80986f11f1daacb043caa203 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 3 Nov 2023 10:40:13 -0400 Subject: [PATCH] Convert EditProfileFragment to kotlin. --- .../profiles/manage/EditProfileFragment.java | 322 ------------------ .../profiles/manage/EditProfileFragment.kt | 291 ++++++++++++++++ .../profiles/manage/UsernameEditViewModel.kt | 7 +- app/src/main/res/values/strings.xml | 6 + 4 files changed, 300 insertions(+), 326 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileFragment.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileFragment.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileFragment.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileFragment.java deleted file mode 100644 index ff3fe94422..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileFragment.java +++ /dev/null @@ -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> 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 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) { - 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; - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileFragment.kt new file mode 100644 index 0000000000..d85a2b46a9 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileFragment.kt @@ -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(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) { + 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) { + 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() + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt index 0fa1d4df08..b2242734bb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt @@ -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.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 } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 816b51c555..93b9ce57e8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -993,6 +993,8 @@ About Failed to set avatar Badges + + QR code or link Edit photo Username created @@ -1002,6 +1004,10 @@ Couldn\'t delete username. Try again later. Username deleted + + Delete username? + + This will remove your username and disable your QR code and link. "%1$s" will be available for others to claim. Are you sure?