diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java index ce4bc3d3d1..fbc0e86b31 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java @@ -14,6 +14,7 @@ public class TooltipValues extends SignalStoreValues { private static final String GROUP_CALL_TOOLTIP_DISPLAY_COUNT = "tooltip.group_call_tooltip_display_count"; private static final String MULTI_FORWARD_DIALOG = "tooltip.multi.forward.dialog"; private static final String BUBBLE_OPT_OUT = "tooltip.bubble.opt.out"; + private static final String PROFILE_SETTINGS_QR_CODE = "tooltip.profile_settings_qr_code"; TooltipValues(@NonNull KeyValueStore store) { @@ -73,4 +74,12 @@ public class TooltipValues extends SignalStoreValues { public void markBubbleOptOutTooltipSeen() { putBoolean(BUBBLE_OPT_OUT, true); } + + public boolean showProfileSettingsQrCodeTooltop() { + return getBoolean(PROFILE_SETTINGS_QR_CODE, true); + } + + public void markProfileSettingsQrCodeTooltipSeen() { + putBoolean(PROFILE_SETTINGS_QR_CODE, false); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/CreateProfileActivity.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/CreateProfileActivity.java index 257bde57b6..abbc383a8b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/CreateProfileActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/CreateProfileActivity.java @@ -52,7 +52,7 @@ public class CreateProfileActivity extends BaseActivity implements CreateProfile setContentView(R.layout.create_profile_activity); if (bundle == null) { - NavHostFragment fragment = NavHostFragment.create(R.navigation.edit_profile, getIntent().getExtras()); + NavHostFragment fragment = NavHostFragment.create(R.navigation.create_profile, getIntent().getExtras()); getSupportFragmentManager().beginTransaction() .add(R.id.fragment_container, fragment) .commit(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditAboutViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditAboutViewModel.java index f451dc9bd6..ab97f5f328 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditAboutViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditAboutViewModel.java @@ -12,12 +12,12 @@ import io.reactivex.rxjava3.subjects.PublishSubject; public final class EditAboutViewModel extends ViewModel { - private final ManageProfileRepository repository; + private final EditProfileRepository repository; private final BehaviorSubject saveState; private final PublishSubject events; public EditAboutViewModel() { - this.repository = new ManageProfileRepository(); + this.repository = new EditProfileRepository(); this.saveState = BehaviorSubject.createDefault(SaveState.IDLE); this.events = PublishSubject.create(); } 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 index d85a2b46a9..9da1ff6f51 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileFragment.kt @@ -14,6 +14,7 @@ import androidx.core.content.res.ResourcesCompat import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.map import androidx.navigation.Navigation.findNavController +import androidx.navigation.fragment.findNavController import com.airbnb.lottie.SimpleColorFilter import com.bumptech.glide.Glide import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -32,9 +33,10 @@ 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.EditProfileViewModel.AvatarState import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.UsernameDeleteResult import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.NameUtil.getAbbreviation import org.thoughtcrime.securesms.util.livedata.LiveDataUtil import org.thoughtcrime.securesms.util.navigation.safeNavigate @@ -49,7 +51,7 @@ class EditProfileFragment : LoggingFragment() { private var avatarProgress: AlertDialog? = null - private lateinit var viewModel: ManageProfileViewModel + private lateinit var viewModel: EditProfileViewModel private lateinit var binding: EditProfileFragmentBinding private lateinit var disposables: LifecycleDisposable @@ -128,10 +130,26 @@ class EditProfileFragment : LoggingFragment() { AvatarPreviewActivity.createTransitionBundle(requireActivity(), binding.manageProfileAvatar) ) } + + if (FeatureFlags.usernames() && SignalStore.account().username != null) { + binding.usernameLinkContainer.setOnClickListener { + findNavController().safeNavigate(EditProfileFragmentDirections.actionManageProfileFragmentToUsernameLinkFragment()) + } + + if (SignalStore.tooltips().showProfileSettingsQrCodeTooltop()) { + binding.usernameLinkTooltip.visibility = View.VISIBLE + binding.linkTooltipCloseButton.setOnClickListener { + binding.usernameLinkTooltip.visibility = View.GONE + SignalStore.tooltips().markProfileSettingsQrCodeTooltipSeen() + } + } + } else { + binding.usernameLinkContainer.visibility = View.GONE + } } private fun initializeViewModel() { - viewModel = ViewModelProvider(this, ManageProfileViewModel.Factory()).get(ManageProfileViewModel::class.java) + viewModel = ViewModelProvider(this, EditProfileViewModel.Factory()).get(EditProfileViewModel::class.java) LiveDataUtil .distinctUntilChanged(viewModel.avatar) { b1, b2 -> Arrays.equals(b1.avatar, b2.avatar) } @@ -185,9 +203,9 @@ class EditProfileFragment : LoggingFragment() { binding.manageProfileAvatarInitials.visibility = View.GONE } - if (avatarProgress == null && avatarState.loadingState == ManageProfileViewModel.LoadingState.LOADING) { + if (avatarProgress == null && avatarState.loadingState == EditProfileViewModel.LoadingState.LOADING) { avatarProgress = SimpleProgressDialog.show(requireContext()) - } else if (avatarProgress != null && avatarState.loadingState == ManageProfileViewModel.LoadingState.LOADED) { + } else if (avatarProgress != null && avatarState.loadingState == EditProfileViewModel.LoadingState.LOADED) { avatarProgress!!.dismiss() } } @@ -251,10 +269,10 @@ class EditProfileFragment : LoggingFragment() { } } - private fun presentEvent(event: ManageProfileViewModel.Event) { + private fun presentEvent(event: EditProfileViewModel.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() + EditProfileViewModel.Event.AVATAR_DISK_FAILURE -> Toast.makeText(requireContext(), R.string.ManageProfileFragment_failed_to_set_avatar, Toast.LENGTH_LONG).show() + EditProfileViewModel.Event.AVATAR_NETWORK_FAILURE -> Toast.makeText(requireContext(), R.string.EditProfileNameFragment_failed_to_save_due_to_network_issues_try_again_later, Toast.LENGTH_LONG).show() } } @@ -284,7 +302,10 @@ class EditProfileFragment : LoggingFragment() { private fun handleUsernameDeletionResult(usernameDeleteResult: UsernameDeleteResult) { when (usernameDeleteResult) { - UsernameDeleteResult.SUCCESS -> Snackbar.make(requireView(), R.string.ManageProfileFragment__username_deleted, Snackbar.LENGTH_SHORT).show() + UsernameDeleteResult.SUCCESS -> { + Snackbar.make(requireView(), R.string.ManageProfileFragment__username_deleted, Snackbar.LENGTH_SHORT).show() + binding.usernameLinkContainer.visibility = View.GONE + } 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/EditProfileNameViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileNameViewModel.java index f944b54c49..b6c71fdffd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileNameViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileNameViewModel.java @@ -14,12 +14,12 @@ import org.signal.core.util.StringUtil; public final class EditProfileNameViewModel extends ViewModel { - private final ManageProfileRepository repository; + private final EditProfileRepository repository; private final MutableLiveData saveState; private final SingleLiveEvent events; public EditProfileNameViewModel() { - this.repository = new ManageProfileRepository(); + this.repository = new EditProfileRepository(); this.saveState = new MutableLiveData<>(SaveState.IDLE); this.events = new SingleLiveEvent<>(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/ManageProfileRepository.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileRepository.java similarity index 96% rename from app/src/main/java/org/thoughtcrime/securesms/profiles/manage/ManageProfileRepository.java rename to app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileRepository.java index a11987e9dc..e004166b35 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/ManageProfileRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileRepository.java @@ -20,9 +20,9 @@ import org.whispersystems.signalservice.api.util.StreamDetails; import java.io.ByteArrayInputStream; import java.io.IOException; -final class ManageProfileRepository { +final class EditProfileRepository { - private static final String TAG = Log.tag(ManageProfileRepository.class); + private static final String TAG = Log.tag(EditProfileRepository.class); public void setName(@NonNull Context context, @NonNull ProfileName profileName, @NonNull Consumer callback) { SignalExecutors.UNBOUNDED.execute(() -> { diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/ManageProfileViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileViewModel.java similarity index 96% rename from app/src/main/java/org/thoughtcrime/securesms/profiles/manage/ManageProfileViewModel.java rename to app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileViewModel.java index 7ea96fb323..2fd708482c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/ManageProfileViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileViewModel.java @@ -37,9 +37,9 @@ import java.util.Optional; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Single; -class ManageProfileViewModel extends ViewModel { +class EditProfileViewModel extends ViewModel { - private static final String TAG = Log.tag(ManageProfileViewModel.class); + private static final String TAG = Log.tag(EditProfileViewModel.class); private final MutableLiveData internalAvatarState; private final MutableLiveData profileName; @@ -49,20 +49,20 @@ class ManageProfileViewModel extends ViewModel { private final LiveData avatarState; private final SingleLiveEvent events; private final RecipientForeverObserver observer; - private final ManageProfileRepository repository; + private final EditProfileRepository repository; private final UsernameRepository usernameEditRepository; private final MutableLiveData> badge; private byte[] previousAvatar; - public ManageProfileViewModel() { + public EditProfileViewModel() { this.internalAvatarState = new MutableLiveData<>(); this.profileName = new MutableLiveData<>(); this.username = new MutableLiveData<>(); this.about = new MutableLiveData<>(); this.aboutEmoji = new MutableLiveData<>(); this.events = new SingleLiveEvent<>(); - this.repository = new ManageProfileRepository(); + this.repository = new EditProfileRepository(); this.usernameEditRepository = new UsernameRepository(); this.badge = new DefaultValueLiveData<>(Optional.empty()); this.observer = this::onRecipientChanged; @@ -281,7 +281,7 @@ class ManageProfileViewModel extends ViewModel { static class Factory extends ViewModelProvider.NewInstanceFactory { @Override public @NonNull T create(@NonNull Class modelClass) { - return Objects.requireNonNull(modelClass.cast(new ManageProfileViewModel())); + return Objects.requireNonNull(modelClass.cast(new EditProfileViewModel())); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt index 6df328a6d2..7234c79e87 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt @@ -232,6 +232,8 @@ class UsernameRepository { return try { accountManager.deleteUsername() SignalDatabase.recipients.setUsername(Recipient.self().id, null) + SignalStore.account().username = null + SignalStore.account().usernameLink = null SignalStore.account().usernameOutOfSync = false Log.i(TAG, "[deleteUsername] Successfully deleted the username.") UsernameDeleteResult.SUCCESS diff --git a/app/src/main/res/drawable/ic_tooltip_arrow_up.xml b/app/src/main/res/drawable/ic_tooltip_arrow_up.xml new file mode 100644 index 0000000000..b3588dd66f --- /dev/null +++ b/app/src/main/res/drawable/ic_tooltip_arrow_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/edit_profile_activity.xml b/app/src/main/res/layout/edit_profile_activity.xml index 1312bbe17f..14f12b993f 100644 --- a/app/src/main/res/layout/edit_profile_activity.xml +++ b/app/src/main/res/layout/edit_profile_activity.xml @@ -17,6 +17,6 @@ app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" - app:navGraph="@navigation/manage_profile" /> + app:navGraph="@navigation/edit_profile" /> diff --git a/app/src/main/res/layout/edit_profile_fragment.xml b/app/src/main/res/layout/edit_profile_fragment.xml index 7c8a7d574b..a04e741393 100644 --- a/app/src/main/res/layout/edit_profile_fragment.xml +++ b/app/src/main/res/layout/edit_profile_fragment.xml @@ -12,7 +12,8 @@ + android:layout_height="wrap_content" + android:animateLayoutChanges="true"> + + + + + + @@ -183,7 +217,7 @@ android:paddingTop="16dp" android:paddingEnd="@dimen/dsl_settings_gutter" android:paddingBottom="16dp" - app:layout_constraintTop_toBottomOf="@id/manage_profile_username_container"> + app:layout_constraintTop_toBottomOf="@id/username_link_container"> + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/create_profile.xml b/app/src/main/res/navigation/create_profile.xml new file mode 100644 index 0000000000..148bf5c975 --- /dev/null +++ b/app/src/main/res/navigation/create_profile.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/edit_profile.xml b/app/src/main/res/navigation/edit_profile.xml index 148bf5c975..dd42e8cb64 100644 --- a/app/src/main/res/navigation/edit_profile.xml +++ b/app/src/main/res/navigation/edit_profile.xml @@ -2,22 +2,47 @@ + android:id="@+id/manage_profile" + app:startDestination="@id/manageProfileFragment"> + android:id="@+id/manageProfileFragment" + android:name="org.thoughtcrime.securesms.profiles.manage.EditProfileFragment" + android:label="fragment_manage_profile" + tools:layout="@layout/edit_profile_fragment"> + + + + + + + app:enterAnim="@anim/fragment_open_enter" + app:exitAnim="@anim/fragment_open_exit" + app:popEnterAnim="@anim/fragment_close_enter" + app:popExitAnim="@anim/fragment_close_exit"> + + + android:id="@+id/action_manageProfileFragment_to_badgeManageFragment" + app:destination="@id/manage_badges" + app:enterAnim="@anim/fragment_open_enter" + app:exitAnim="@anim/fragment_open_exit" + app:popEnterAnim="@anim/fragment_close_enter" + app:popExitAnim="@anim/fragment_close_exit" /> + + + + + + - + + android:id="@+id/usernameEducationFragment" + android:name="org.thoughtcrime.securesms.profiles.manage.UsernameEducationFragment" + android:label="fragment_username_education" + tools:layout="@layout/username_education_fragment"> + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/manage_profile.xml b/app/src/main/res/navigation/manage_profile.xml deleted file mode 100644 index a195fa9557..0000000000 --- a/app/src/main/res/navigation/manage_profile.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 93b9ce57e8..72842cf9ec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -996,6 +996,10 @@ QR code or link Edit photo + + Share your username + + Let others start a chat with you by sharing your unique QR code or link. Username created