mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-24 03:35:58 +00:00
Add capability to request username creation during registration.
This commit is contained in:
committed by
Greyson Parrelli
parent
7e45fc4a3e
commit
977af2c2f3
@@ -20,17 +20,20 @@ import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceTransferActivity;
|
||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
|
||||
import org.thoughtcrime.securesms.migrations.ApplicationMigrationActivity;
|
||||
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
|
||||
import org.thoughtcrime.securesms.pin.PinRestoreActivity;
|
||||
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
|
||||
import org.thoughtcrime.securesms.profiles.username.AddAUsernameActivity;
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.AppStartup;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.Locale;
|
||||
@@ -52,6 +55,7 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
|
||||
private static final int STATE_TRANSFER_ONGOING = 8;
|
||||
private static final int STATE_TRANSFER_LOCKED = 9;
|
||||
private static final int STATE_CHANGE_NUMBER_LOCK = 10;
|
||||
private static final int STATE_CREATE_USERNAME = 11;
|
||||
|
||||
private SignalServiceNetworkAccess networkAccess;
|
||||
private BroadcastReceiver clearKeyReceiver;
|
||||
@@ -156,6 +160,7 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
|
||||
case STATE_TRANSFER_ONGOING: return getOldDeviceTransferIntent();
|
||||
case STATE_TRANSFER_LOCKED: return getOldDeviceTransferLockedIntent();
|
||||
case STATE_CHANGE_NUMBER_LOCK: return getChangeNumberLockIntent();
|
||||
case STATE_CREATE_USERNAME: return getCreateUsernameIntent();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
@@ -175,6 +180,8 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
|
||||
return STATE_CREATE_SIGNAL_PIN;
|
||||
} else if (userMustSetProfileName()) {
|
||||
return STATE_CREATE_PROFILE_NAME;
|
||||
} else if (shouldAskUserToCreateUsername()) {
|
||||
return STATE_CREATE_USERNAME;
|
||||
} else if (userMustCreateSignalPin()) {
|
||||
return STATE_CREATE_SIGNAL_PIN;
|
||||
} else if (EventBus.getDefault().getStickyEvent(TransferStatus.class) != null && getClass() != OldDeviceTransferActivity.class) {
|
||||
@@ -200,6 +207,13 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
|
||||
return !SignalStore.registrationValues().isRegistrationComplete() && Recipient.self().getProfileName().isEmpty();
|
||||
}
|
||||
|
||||
private boolean shouldAskUserToCreateUsername() {
|
||||
return FeatureFlags.usernames() &&
|
||||
FeatureFlags.phoneNumberPrivacy() &&
|
||||
!SignalStore.uiHints().hasSetOrSkippedUsernameCreation() &&
|
||||
SignalStore.phoneNumberPrivacy().getPhoneNumberListingMode() == PhoneNumberPrivacyValues.PhoneNumberListingMode.UNLISTED;
|
||||
}
|
||||
|
||||
private Intent getCreatePassphraseIntent() {
|
||||
return getRoutedIntent(PassphraseCreateActivity.class, getIntent());
|
||||
}
|
||||
@@ -259,6 +273,10 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
|
||||
return ChangeNumberLockActivity.createIntent(this);
|
||||
}
|
||||
|
||||
private Intent getCreateUsernameIntent() {
|
||||
return getRoutedIntent(AddAUsernameActivity.class, getIntent());
|
||||
}
|
||||
|
||||
private Intent getRoutedIntent(Intent destination, @Nullable Intent nextIntent) {
|
||||
if (nextIntent != null) destination.putExtra("next_intent", nextIntent);
|
||||
return destination;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.thoughtcrime.securesms.components
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import org.thoughtcrime.securesms.R
|
||||
|
||||
/**
|
||||
* A small card with a circular progress indicator in it. Usable in place
|
||||
* of a ProgressDialog, which is deprecated.
|
||||
*
|
||||
* Remember to add this as the last UI element in your XML hierarchy so it'll
|
||||
* draw over top of other elements.
|
||||
*/
|
||||
class ProgressCard @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : MaterialCardView(context, attrs) {
|
||||
init {
|
||||
inflate(context, R.layout.progress_card, this)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ public class UiHints extends SignalStoreValues {
|
||||
|
||||
private static final String HAS_SEEN_GROUP_SETTINGS_MENU_TOAST = "uihints.has_seen_group_settings_menu_toast";
|
||||
private static final String HAS_CONFIRMED_DELETE_FOR_EVERYONE_ONCE = "uihints.has_confirmed_delete_for_everyone_once";
|
||||
private static final String HAS_SET_OR_SKIPPED_USERNAME_CREATION = "uihints.has_set_or_skipped_username_creation";
|
||||
|
||||
UiHints(@NonNull KeyValueStore store) {
|
||||
super(store);
|
||||
@@ -39,4 +40,12 @@ public class UiHints extends SignalStoreValues {
|
||||
public boolean hasConfirmedDeleteForEveryoneOnce() {
|
||||
return getBoolean(HAS_CONFIRMED_DELETE_FOR_EVERYONE_ONCE, false);
|
||||
}
|
||||
|
||||
public boolean hasSetOrSkippedUsernameCreation() {
|
||||
return getBoolean(HAS_SET_OR_SKIPPED_USERNAME_CREATION, false);
|
||||
}
|
||||
|
||||
public void markHasSetOrSkippedUsernameCreation() {
|
||||
putBoolean(HAS_SET_OR_SKIPPED_USERNAME_CREATION, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.thoughtcrime.securesms.profiles.edit;
|
||||
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
@@ -20,7 +19,6 @@ import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
/**
|
||||
* Shows editing screen for your profile during registration. Also handles group name editing.
|
||||
*/
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public class EditProfileActivity extends BaseActivity implements EditProfileFragment.Controller {
|
||||
|
||||
public static final String NEXT_INTENT = "next_intent";
|
||||
@@ -37,13 +35,6 @@ public class EditProfileActivity extends BaseActivity implements EditProfileFrag
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static @NonNull Intent getIntentForUserProfileEdit(@NonNull Context context) {
|
||||
Intent intent = new Intent(context, EditProfileActivity.class);
|
||||
intent.putExtra(EditProfileActivity.EXCLUDE_SYSTEM, true);
|
||||
intent.putExtra(EditProfileActivity.NEXT_BUTTON_TEXT, R.string.save);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static @NonNull Intent getIntentForGroupProfile(@NonNull Context context, @NonNull GroupId groupId) {
|
||||
Intent intent = new Intent(context, EditProfileActivity.class);
|
||||
intent.putExtra(EditProfileActivity.SHOW_TOOLBAR, true);
|
||||
|
||||
@@ -18,6 +18,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
@@ -91,7 +92,7 @@ public class EditProfileFragment extends LoggingFragment {
|
||||
GroupId groupId = GroupId.parseNullableOrThrow(requireArguments().getString(GROUP_ID, null));
|
||||
|
||||
initializeViewModel(requireArguments().getBoolean(EXCLUDE_SYSTEM, false), groupId, savedInstanceState != null);
|
||||
initializeResources(view, groupId);
|
||||
initializeResources(groupId);
|
||||
initializeProfileAvatar();
|
||||
initializeProfileName();
|
||||
|
||||
@@ -151,11 +152,10 @@ public class EditProfileFragment extends LoggingFragment {
|
||||
|
||||
EditProfileViewModel.Factory factory = new EditProfileViewModel.Factory(repository, hasSavedInstanceState, groupId);
|
||||
|
||||
viewModel = ViewModelProviders.of(requireActivity(), factory)
|
||||
.get(EditProfileViewModel.class);
|
||||
viewModel = new ViewModelProvider(requireActivity(), factory).get(EditProfileViewModel.class);
|
||||
}
|
||||
|
||||
private void initializeResources(@NonNull View view, @Nullable GroupId groupId) {
|
||||
private void initializeResources(@Nullable GroupId groupId) {
|
||||
Bundle arguments = requireArguments();
|
||||
boolean isEditingGroup = groupId != null;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.thoughtcrime.securesms.profiles.manage;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -17,6 +17,7 @@ import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.Navigation;
|
||||
@@ -29,10 +30,12 @@ import com.google.android.material.textfield.TextInputLayout;
|
||||
|
||||
import org.signal.core.util.DimensionUnit;
|
||||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
|
||||
import org.thoughtcrime.securesms.databinding.UsernameEditFragmentBinding;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.AccessibilityUtil;
|
||||
import org.thoughtcrime.securesms.util.FragmentResultContract;
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable;
|
||||
import org.thoughtcrime.securesms.util.UsernameUtil;
|
||||
@@ -49,6 +52,7 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||
private UsernameEditFragmentBinding binding;
|
||||
private ImageView suffixProgress;
|
||||
private LifecycleDisposable lifecycleDisposable;
|
||||
private UsernameEditFragmentArgs args;
|
||||
|
||||
public static UsernameEditFragment newInstance() {
|
||||
return new UsernameEditFragment();
|
||||
@@ -62,20 +66,37 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
binding.toolbar.setNavigationOnClickListener(v -> Navigation.findNavController(view).popBackStack());
|
||||
Bundle bundle = getArguments();
|
||||
if (bundle != null) {
|
||||
args = UsernameEditFragmentArgs.fromBundle(bundle);
|
||||
} else {
|
||||
args = new UsernameEditFragmentArgs.Builder().build();
|
||||
}
|
||||
|
||||
if (args.getIsInRegistration()) {
|
||||
binding.toolbar.setNavigationIcon(null);
|
||||
binding.toolbar.setTitle(R.string.UsernameEditFragment__add_a_username);
|
||||
binding.usernameSkipButton.setVisibility(View.VISIBLE);
|
||||
binding.usernameDoneButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.toolbar.setNavigationOnClickListener(v -> Navigation.findNavController(view).popBackStack());
|
||||
binding.usernameSubmitButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
binding.usernameTextWrapper.setErrorIconDrawable(null);
|
||||
|
||||
lifecycleDisposable = new LifecycleDisposable();
|
||||
lifecycleDisposable.bindTo(getViewLifecycleOwner());
|
||||
|
||||
viewModel = new ViewModelProvider(this, new UsernameEditViewModel.Factory()).get(UsernameEditViewModel.class);
|
||||
viewModel = new ViewModelProvider(this, new UsernameEditViewModel.Factory(args.getIsInRegistration())).get(UsernameEditViewModel.class);
|
||||
|
||||
lifecycleDisposable.add(viewModel.getUiState().subscribe(this::onUiStateChanged));
|
||||
viewModel.getEvents().observe(getViewLifecycleOwner(), this::onEvent);
|
||||
|
||||
binding.usernameSubmitButton.setOnClickListener(v -> viewModel.onUsernameSubmitted());
|
||||
binding.usernameDeleteButton.setOnClickListener(v -> viewModel.onUsernameDeleted());
|
||||
binding.usernameDoneButton.setOnClickListener(v -> viewModel.onUsernameSubmitted());
|
||||
binding.usernameSkipButton.setOnClickListener(v -> viewModel.onUsernameSkipped());
|
||||
|
||||
UsernameState usernameState = Recipient.self().getUsername().<UsernameState>map(UsernameState.Set::new).orElse(UsernameState.NoUsername.INSTANCE);
|
||||
binding.usernameText.setText(usernameState.getNickname());
|
||||
@@ -142,15 +163,82 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||
}
|
||||
|
||||
private void onUiStateChanged(@NonNull UsernameEditViewModel.State state) {
|
||||
EditText usernameInput = binding.usernameText;
|
||||
TextInputLayout usernameInputWrapper = binding.usernameTextWrapper;
|
||||
|
||||
presentSuffix(state.getUsername());
|
||||
presentButtonState(state.getButtonState());
|
||||
|
||||
switch (state.getUsernameStatus()) {
|
||||
case NONE:
|
||||
usernameInputWrapper.setError(null);
|
||||
break;
|
||||
case TOO_SHORT:
|
||||
case TOO_LONG:
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_must_be_between_a_and_b_characters, UsernameUtil.MIN_LENGTH, UsernameUtil.MAX_LENGTH));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
case INVALID_CHARACTERS:
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_can_only_include));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
case CANNOT_START_WITH_NUMBER:
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_cannot_begin_with_a_number));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
case INVALID_GENERIC:
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_username_is_invalid));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
case TAKEN:
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_this_username_is_taken));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void presentButtonState(@NonNull UsernameEditViewModel.ButtonState buttonState) {
|
||||
if (args.getIsInRegistration()) {
|
||||
presentRegistrationButtonState(buttonState);
|
||||
} else {
|
||||
presentProfileUpdateButtonState(buttonState);
|
||||
}
|
||||
}
|
||||
|
||||
private void presentRegistrationButtonState(@NonNull UsernameEditViewModel.ButtonState buttonState) {
|
||||
binding.usernameText.setEnabled(true);
|
||||
binding.usernameProgressCard.setVisibility(View.GONE);
|
||||
|
||||
switch (buttonState) {
|
||||
case SUBMIT:
|
||||
binding.usernameDoneButton.setEnabled(true);
|
||||
binding.usernameDoneButton.setAlpha(1f);
|
||||
break;
|
||||
case SUBMIT_DISABLED:
|
||||
binding.usernameDoneButton.setEnabled(false);
|
||||
binding.usernameDoneButton.setAlpha(DISABLED_ALPHA);
|
||||
break;
|
||||
case SUBMIT_LOADING:
|
||||
binding.usernameDoneButton.setEnabled(false);
|
||||
binding.usernameDoneButton.setAlpha(DISABLED_ALPHA);
|
||||
binding.usernameProgressCard.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Delete functionality is not available during registration.");
|
||||
}
|
||||
}
|
||||
|
||||
private void presentProfileUpdateButtonState(@NonNull UsernameEditViewModel.ButtonState buttonState) {
|
||||
CircularProgressMaterialButton submitButton = binding.usernameSubmitButton;
|
||||
CircularProgressMaterialButton deleteButton = binding.usernameDeleteButton;
|
||||
TextInputLayout usernameInputWrapper = binding.usernameTextWrapper;
|
||||
EditText usernameInput = binding.usernameText;
|
||||
|
||||
usernameInput.setEnabled(true);
|
||||
presentSuffix(state.getUsername());
|
||||
|
||||
switch (state.getButtonState()) {
|
||||
switch (buttonState) {
|
||||
case SUBMIT:
|
||||
submitButton.cancelSpinning();
|
||||
submitButton.setVisibility(View.VISIBLE);
|
||||
@@ -194,38 +282,6 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||
usernameInput.setEnabled(false);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (state.getUsernameStatus()) {
|
||||
case NONE:
|
||||
usernameInputWrapper.setError(null);
|
||||
break;
|
||||
case TOO_SHORT:
|
||||
case TOO_LONG:
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_must_be_between_a_and_b_characters, UsernameUtil.MIN_LENGTH, UsernameUtil.MAX_LENGTH));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
case INVALID_CHARACTERS:
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_can_only_include));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
case CANNOT_START_WITH_NUMBER:
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_cannot_begin_with_a_number));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
case INVALID_GENERIC:
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_username_is_invalid));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
case TAKEN:
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_this_username_is_taken));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void presentSuffix(@NonNull UsernameState usernameState) {
|
||||
@@ -257,7 +313,7 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||
switch (event) {
|
||||
case SUBMIT_SUCCESS:
|
||||
ResultContract.setUsernameCreated(getParentFragmentManager());
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
closeScreen();
|
||||
break;
|
||||
case SUBMIT_FAIL_TAKEN:
|
||||
Toast.makeText(requireContext(), R.string.UsernameEditFragment_this_username_is_taken, Toast.LENGTH_SHORT).show();
|
||||
@@ -272,6 +328,36 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||
case NETWORK_FAILURE:
|
||||
Toast.makeText(requireContext(), R.string.UsernameEditFragment_encountered_a_network_error, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case SKIPPED:
|
||||
closeScreen();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void closeScreen() {
|
||||
if (args.getIsInRegistration()) {
|
||||
finishAndStartNextIntent();
|
||||
} else {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
}
|
||||
|
||||
private void finishAndStartNextIntent() {
|
||||
FragmentActivity activity = requireActivity();
|
||||
boolean didLaunch = false;
|
||||
Intent activityIntent = activity.getIntent();
|
||||
|
||||
if (activityIntent != null) {
|
||||
Intent nextIntent = activityIntent.getParcelableExtra(PassphraseRequiredActivity.NEXT_INTENT_EXTRA);
|
||||
if (nextIntent != null) {
|
||||
activity.startActivity(nextIntent);
|
||||
activity.finish();
|
||||
didLaunch = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!didLaunch) {
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
import org.thoughtcrime.securesms.util.UsernameUtil;
|
||||
@@ -45,13 +46,15 @@ class UsernameEditViewModel extends ViewModel {
|
||||
private final RxStore<State> uiState;
|
||||
private final PublishProcessor<String> nicknamePublisher;
|
||||
private final CompositeDisposable disposables;
|
||||
private final boolean isInRegistration;
|
||||
|
||||
private UsernameEditViewModel() {
|
||||
private UsernameEditViewModel(boolean isInRegistration) {
|
||||
this.repo = new UsernameEditRepository();
|
||||
this.uiState = new RxStore<>(new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, Recipient.self().getUsername().<UsernameState>map(UsernameState.Set::new).orElse(UsernameState.NoUsername.INSTANCE)), Schedulers.computation());
|
||||
this.events = new SingleLiveEvent<>();
|
||||
this.nicknamePublisher = PublishProcessor.create();
|
||||
this.disposables = new CompositeDisposable();
|
||||
this.isInRegistration = isInRegistration;
|
||||
|
||||
Disposable disposable = nicknamePublisher.debounce(NICKNAME_PUBLISHER_DEBOUNCE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
|
||||
.subscribe(this::onNicknameChanged);
|
||||
@@ -67,7 +70,7 @@ class UsernameEditViewModel extends ViewModel {
|
||||
void onNicknameUpdated(@NonNull String nickname) {
|
||||
uiState.update(state -> {
|
||||
if (TextUtils.isEmpty(nickname) && Recipient.self().getUsername().isPresent()) {
|
||||
return new State(ButtonState.DELETE, UsernameStatus.NONE, UsernameState.NoUsername.INSTANCE);
|
||||
return new State(isInRegistration ? ButtonState.SUBMIT_DISABLED : ButtonState.DELETE, UsernameStatus.NONE, UsernameState.NoUsername.INSTANCE);
|
||||
}
|
||||
|
||||
Optional<InvalidReason> invalidReason = UsernameUtil.checkUsername(nickname);
|
||||
@@ -79,6 +82,11 @@ class UsernameEditViewModel extends ViewModel {
|
||||
nicknamePublisher.onNext(nickname);
|
||||
}
|
||||
|
||||
void onUsernameSkipped() {
|
||||
SignalStore.uiHints().markHasSetOrSkippedUsernameCreation();
|
||||
events.setValue(Event.SKIPPED);
|
||||
}
|
||||
|
||||
void onUsernameSubmitted() {
|
||||
UsernameState usernameState = uiState.getState().getUsername();
|
||||
|
||||
@@ -107,6 +115,7 @@ class UsernameEditViewModel extends ViewModel {
|
||||
|
||||
switch (result) {
|
||||
case SUCCESS:
|
||||
SignalStore.uiHints().markHasSetOrSkippedUsernameCreation();
|
||||
uiState.update(state -> new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, state.usernameState));
|
||||
events.postValue(Event.SUBMIT_SUCCESS);
|
||||
break;
|
||||
@@ -248,14 +257,21 @@ class UsernameEditViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
enum Event {
|
||||
NETWORK_FAILURE, SUBMIT_SUCCESS, DELETE_SUCCESS, SUBMIT_FAIL_INVALID, SUBMIT_FAIL_TAKEN
|
||||
NETWORK_FAILURE, SUBMIT_SUCCESS, DELETE_SUCCESS, SUBMIT_FAIL_INVALID, SUBMIT_FAIL_TAKEN, SKIPPED
|
||||
}
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
||||
private final boolean isInRegistration;
|
||||
|
||||
Factory(boolean isInRegistration) {
|
||||
this.isInRegistration = isInRegistration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
//noinspection ConstantConditions
|
||||
return modelClass.cast(new UsernameEditViewModel());
|
||||
return modelClass.cast(new UsernameEditViewModel(isInRegistration));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.thoughtcrime.securesms.profiles.username
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import org.thoughtcrime.securesms.BaseActivity
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.profiles.manage.UsernameEditFragmentArgs
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme
|
||||
|
||||
class AddAUsernameActivity : BaseActivity() {
|
||||
protected open val dynamicTheme: DynamicTheme = DynamicNoActionBarTheme()
|
||||
protected open val contentViewId: Int = R.layout.fragment_container
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(contentViewId)
|
||||
dynamicTheme.onCreate(this)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(
|
||||
R.id.fragment_container,
|
||||
NavHostFragment.create(
|
||||
R.navigation.create_username,
|
||||
UsernameEditFragmentArgs.Builder().setIsInRegistration(true).build().toBundle()
|
||||
)
|
||||
)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
dynamicTheme.onResume(this)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user