mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Add username education screen.
This commit is contained in:
committed by
Greyson Parrelli
parent
dae69744c2
commit
4f387cf8d9
@@ -13,6 +13,7 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
@@ -63,8 +64,13 @@ public class ManageProfileActivity extends PassphraseRequiredActivity implements
|
||||
navController.setGraph(graph, extras != null ? extras : new Bundle());
|
||||
|
||||
if (extras != null && extras.getBoolean(START_AT_USERNAME, false)) {
|
||||
NavDirections action = ManageProfileFragmentDirections.actionManageUsername();
|
||||
SafeNavigation.safeNavigate(navController, action);
|
||||
if (SignalStore.uiHints().hasSeenUsernameEducation()) {
|
||||
NavDirections action = ManageProfileFragmentDirections.actionManageUsername();
|
||||
SafeNavigation.safeNavigate(navController, action);
|
||||
} else {
|
||||
NavDirections action = ManageProfileFragmentDirections.actionManageProfileFragmentToUsernameEducationFragment();
|
||||
SafeNavigation.safeNavigate(navController, action);
|
||||
}
|
||||
}
|
||||
|
||||
if (extras != null && extras.getBoolean(START_AT_AVATAR, false)) {
|
||||
|
||||
@@ -20,6 +20,7 @@ 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;
|
||||
@@ -33,11 +34,12 @@ 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.ManageProfileFragmentBinding;
|
||||
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.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable;
|
||||
import org.thoughtcrime.securesms.util.NameUtil;
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
|
||||
@@ -49,6 +51,8 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
public class ManageProfileFragment extends LoggingFragment {
|
||||
|
||||
private static final String TAG = Log.tag(ManageProfileFragment.class);
|
||||
@@ -56,6 +60,7 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||
private AlertDialog avatarProgress;
|
||||
private ManageProfileViewModel viewModel;
|
||||
private ManageProfileFragmentBinding binding;
|
||||
private LifecycleDisposable disposables;
|
||||
|
||||
@Override
|
||||
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
@@ -66,6 +71,9 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||
|
||||
@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();
|
||||
});
|
||||
@@ -85,7 +93,28 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||
});
|
||||
|
||||
binding.manageProfileUsernameContainer.setOnClickListener(v -> {
|
||||
SafeNavigation.safeNavigate(Navigation.findNavController(v), ManageProfileFragmentDirections.actionManageUsername());
|
||||
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), ManageProfileFragmentDirections.actionManageUsername());
|
||||
break;
|
||||
case 1:
|
||||
displayConfirmUsernameDeletionDialog();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
} else {
|
||||
SafeNavigation.safeNavigate(Navigation.findNavController(v), ManageProfileFragmentDirections.actionManageUsername());
|
||||
}
|
||||
} else {
|
||||
SafeNavigation.safeNavigate(Navigation.findNavController(v), ManageProfileFragmentDirections.actionManageProfileFragmentToUsernameEducationFragment());
|
||||
}
|
||||
});
|
||||
|
||||
binding.manageProfileAboutContainer.setOnClickListener(v -> {
|
||||
@@ -272,4 +301,29 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||
private void onEditAvatarClicked() {
|
||||
SafeNavigation.safeNavigate(Navigation.findNavController(requireView()), ManageProfileFragmentDirections.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 UsernameEditRepository.UsernameDeleteResult usernameDeleteResult) {
|
||||
// TODO [alex] -- Snackbar?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
|
||||
class ManageProfileViewModel extends ViewModel {
|
||||
|
||||
private static final String TAG = Log.tag(ManageProfileViewModel.class);
|
||||
@@ -46,22 +49,24 @@ class ManageProfileViewModel extends ViewModel {
|
||||
private final LiveData<AvatarState> avatarState;
|
||||
private final SingleLiveEvent<Event> events;
|
||||
private final RecipientForeverObserver observer;
|
||||
private final ManageProfileRepository repository;
|
||||
private final MutableLiveData<Optional<Badge>> badge;
|
||||
private final ManageProfileRepository repository;
|
||||
private final UsernameEditRepository usernameEditRepository;
|
||||
private final MutableLiveData<Optional<Badge>> badge;
|
||||
|
||||
private byte[] previousAvatar;
|
||||
|
||||
public ManageProfileViewModel() {
|
||||
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.badge = new DefaultValueLiveData<>(Optional.empty());
|
||||
this.observer = this::onRecipientChanged;
|
||||
this.avatarState = LiveDataUtil.combineLatest(Recipient.self().live().getLiveData(), internalAvatarState, (self, state) -> new AvatarState(state, self));
|
||||
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.usernameEditRepository = new UsernameEditRepository();
|
||||
this.badge = new DefaultValueLiveData<>(Optional.empty());
|
||||
this.observer = this::onRecipientChanged;
|
||||
this.avatarState = LiveDataUtil.combineLatest(Recipient.self().live().getLiveData(), internalAvatarState, (self, state) -> new AvatarState(state, self));
|
||||
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
onRecipientChanged(Recipient.self().fresh());
|
||||
@@ -99,6 +104,10 @@ class ManageProfileViewModel extends ViewModel {
|
||||
return events;
|
||||
}
|
||||
|
||||
public Single<UsernameEditRepository.UsernameDeleteResult> deleteUsername() {
|
||||
return usernameEditRepository.deleteUsername().observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public boolean shouldShowUsername() {
|
||||
return FeatureFlags.usernames();
|
||||
}
|
||||
@@ -271,7 +280,7 @@ class ManageProfileViewModel extends ViewModel {
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
@Override
|
||||
public @NonNull<T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
return Objects.requireNonNull(modelClass.cast(new ManageProfileViewModel()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.signal.core.util.Result;
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
@@ -17,7 +16,6 @@ import org.whispersystems.signalservice.api.push.exceptions.UsernameTakenExcepti
|
||||
import org.whispersystems.signalservice.internal.push.ReserveUsernameResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.thoughtcrime.securesms.profiles.manage
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.ViewBinderDelegate
|
||||
import org.thoughtcrime.securesms.databinding.UsernameEducationFragmentBinding
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
||||
/**
|
||||
* Displays a Username education screen which displays some basic information
|
||||
* about usernames and provides a learn-more link.
|
||||
*/
|
||||
class UsernameEducationFragment : Fragment(R.layout.username_education_fragment) {
|
||||
private val binding by ViewBinderDelegate(UsernameEducationFragmentBinding::bind)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
|
||||
binding.usernameEducationLearnMore.setOnClickListener {
|
||||
// TODO [alex] -- Launch "Learn More" page.
|
||||
}
|
||||
|
||||
binding.continueButton.setOnClickListener {
|
||||
SignalStore.uiHints().markHasSeenUsernameEducation()
|
||||
findNavController().safeNavigate(UsernameEducationFragmentDirections.actionUsernameEducationFragmentToUsernameManageFragment())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user