kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add capability to request username creation during registration.
rodzic
7e45fc4a3e
commit
977af2c2f3
|
@ -570,6 +570,10 @@
|
||||||
android:theme="@style/TextSecure.LightRegistrationTheme"
|
android:theme="@style/TextSecure.LightRegistrationTheme"
|
||||||
android:windowSoftInputMode="stateVisible|adjustResize" />
|
android:windowSoftInputMode="stateVisible|adjustResize" />
|
||||||
|
|
||||||
|
<activity android:name=".profiles.username.AddAUsernameActivity"
|
||||||
|
android:theme="@style/Signal.DayNight.NoActionBar"
|
||||||
|
android:windowSoftInputMode="stateVisible|adjustResize" />
|
||||||
|
|
||||||
<activity android:name=".profiles.manage.ManageProfileActivity"
|
<activity android:name=".profiles.manage.ManageProfileActivity"
|
||||||
android:theme="@style/TextSecure.LightTheme"
|
android:theme="@style/TextSecure.LightTheme"
|
||||||
android:windowSoftInputMode="stateVisible|adjustResize" />
|
android:windowSoftInputMode="stateVisible|adjustResize" />
|
||||||
|
|
|
@ -20,17 +20,20 @@ import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceTransferActivity;
|
import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceTransferActivity;
|
||||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
|
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
|
||||||
import org.thoughtcrime.securesms.migrations.ApplicationMigrationActivity;
|
import org.thoughtcrime.securesms.migrations.ApplicationMigrationActivity;
|
||||||
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
|
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
|
||||||
import org.thoughtcrime.securesms.pin.PinRestoreActivity;
|
import org.thoughtcrime.securesms.pin.PinRestoreActivity;
|
||||||
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
|
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
|
||||||
|
import org.thoughtcrime.securesms.profiles.username.AddAUsernameActivity;
|
||||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
|
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
import org.thoughtcrime.securesms.util.AppStartup;
|
import org.thoughtcrime.securesms.util.AppStartup;
|
||||||
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
|
||||||
import java.util.Locale;
|
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_ONGOING = 8;
|
||||||
private static final int STATE_TRANSFER_LOCKED = 9;
|
private static final int STATE_TRANSFER_LOCKED = 9;
|
||||||
private static final int STATE_CHANGE_NUMBER_LOCK = 10;
|
private static final int STATE_CHANGE_NUMBER_LOCK = 10;
|
||||||
|
private static final int STATE_CREATE_USERNAME = 11;
|
||||||
|
|
||||||
private SignalServiceNetworkAccess networkAccess;
|
private SignalServiceNetworkAccess networkAccess;
|
||||||
private BroadcastReceiver clearKeyReceiver;
|
private BroadcastReceiver clearKeyReceiver;
|
||||||
|
@ -156,6 +160,7 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
|
||||||
case STATE_TRANSFER_ONGOING: return getOldDeviceTransferIntent();
|
case STATE_TRANSFER_ONGOING: return getOldDeviceTransferIntent();
|
||||||
case STATE_TRANSFER_LOCKED: return getOldDeviceTransferLockedIntent();
|
case STATE_TRANSFER_LOCKED: return getOldDeviceTransferLockedIntent();
|
||||||
case STATE_CHANGE_NUMBER_LOCK: return getChangeNumberLockIntent();
|
case STATE_CHANGE_NUMBER_LOCK: return getChangeNumberLockIntent();
|
||||||
|
case STATE_CREATE_USERNAME: return getCreateUsernameIntent();
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,6 +180,8 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
|
||||||
return STATE_CREATE_SIGNAL_PIN;
|
return STATE_CREATE_SIGNAL_PIN;
|
||||||
} else if (userMustSetProfileName()) {
|
} else if (userMustSetProfileName()) {
|
||||||
return STATE_CREATE_PROFILE_NAME;
|
return STATE_CREATE_PROFILE_NAME;
|
||||||
|
} else if (shouldAskUserToCreateUsername()) {
|
||||||
|
return STATE_CREATE_USERNAME;
|
||||||
} else if (userMustCreateSignalPin()) {
|
} else if (userMustCreateSignalPin()) {
|
||||||
return STATE_CREATE_SIGNAL_PIN;
|
return STATE_CREATE_SIGNAL_PIN;
|
||||||
} else if (EventBus.getDefault().getStickyEvent(TransferStatus.class) != null && getClass() != OldDeviceTransferActivity.class) {
|
} 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();
|
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() {
|
private Intent getCreatePassphraseIntent() {
|
||||||
return getRoutedIntent(PassphraseCreateActivity.class, getIntent());
|
return getRoutedIntent(PassphraseCreateActivity.class, getIntent());
|
||||||
}
|
}
|
||||||
|
@ -259,6 +273,10 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
|
||||||
return ChangeNumberLockActivity.createIntent(this);
|
return ChangeNumberLockActivity.createIntent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Intent getCreateUsernameIntent() {
|
||||||
|
return getRoutedIntent(AddAUsernameActivity.class, getIntent());
|
||||||
|
}
|
||||||
|
|
||||||
private Intent getRoutedIntent(Intent destination, @Nullable Intent nextIntent) {
|
private Intent getRoutedIntent(Intent destination, @Nullable Intent nextIntent) {
|
||||||
if (nextIntent != null) destination.putExtra("next_intent", nextIntent);
|
if (nextIntent != null) destination.putExtra("next_intent", nextIntent);
|
||||||
return destination;
|
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_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_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) {
|
UiHints(@NonNull KeyValueStore store) {
|
||||||
super(store);
|
super(store);
|
||||||
|
@ -39,4 +40,12 @@ public class UiHints extends SignalStoreValues {
|
||||||
public boolean hasConfirmedDeleteForEveryoneOnce() {
|
public boolean hasConfirmedDeleteForEveryoneOnce() {
|
||||||
return getBoolean(HAS_CONFIRMED_DELETE_FOR_EVERYONE_ONCE, false);
|
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;
|
package org.thoughtcrime.securesms.profiles.edit;
|
||||||
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
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.
|
* Shows editing screen for your profile during registration. Also handles group name editing.
|
||||||
*/
|
*/
|
||||||
@SuppressLint("StaticFieldLeak")
|
|
||||||
public class EditProfileActivity extends BaseActivity implements EditProfileFragment.Controller {
|
public class EditProfileActivity extends BaseActivity implements EditProfileFragment.Controller {
|
||||||
|
|
||||||
public static final String NEXT_INTENT = "next_intent";
|
public static final String NEXT_INTENT = "next_intent";
|
||||||
|
@ -37,13 +35,6 @@ public class EditProfileActivity extends BaseActivity implements EditProfileFrag
|
||||||
return intent;
|
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) {
|
public static @NonNull Intent getIntentForGroupProfile(@NonNull Context context, @NonNull GroupId groupId) {
|
||||||
Intent intent = new Intent(context, EditProfileActivity.class);
|
Intent intent = new Intent(context, EditProfileActivity.class);
|
||||||
intent.putExtra(EditProfileActivity.SHOW_TOOLBAR, true);
|
intent.putExtra(EditProfileActivity.SHOW_TOOLBAR, true);
|
||||||
|
|
|
@ -18,6 +18,7 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.lifecycle.ViewModelProviders;
|
import androidx.lifecycle.ViewModelProviders;
|
||||||
import androidx.navigation.Navigation;
|
import androidx.navigation.Navigation;
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ public class EditProfileFragment extends LoggingFragment {
|
||||||
GroupId groupId = GroupId.parseNullableOrThrow(requireArguments().getString(GROUP_ID, null));
|
GroupId groupId = GroupId.parseNullableOrThrow(requireArguments().getString(GROUP_ID, null));
|
||||||
|
|
||||||
initializeViewModel(requireArguments().getBoolean(EXCLUDE_SYSTEM, false), groupId, savedInstanceState != null);
|
initializeViewModel(requireArguments().getBoolean(EXCLUDE_SYSTEM, false), groupId, savedInstanceState != null);
|
||||||
initializeResources(view, groupId);
|
initializeResources(groupId);
|
||||||
initializeProfileAvatar();
|
initializeProfileAvatar();
|
||||||
initializeProfileName();
|
initializeProfileName();
|
||||||
|
|
||||||
|
@ -151,11 +152,10 @@ public class EditProfileFragment extends LoggingFragment {
|
||||||
|
|
||||||
EditProfileViewModel.Factory factory = new EditProfileViewModel.Factory(repository, hasSavedInstanceState, groupId);
|
EditProfileViewModel.Factory factory = new EditProfileViewModel.Factory(repository, hasSavedInstanceState, groupId);
|
||||||
|
|
||||||
viewModel = ViewModelProviders.of(requireActivity(), factory)
|
viewModel = new ViewModelProvider(requireActivity(), factory).get(EditProfileViewModel.class);
|
||||||
.get(EditProfileViewModel.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeResources(@NonNull View view, @Nullable GroupId groupId) {
|
private void initializeResources(@Nullable GroupId groupId) {
|
||||||
Bundle arguments = requireArguments();
|
Bundle arguments = requireArguments();
|
||||||
boolean isEditingGroup = groupId != null;
|
boolean isEditingGroup = groupId != null;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.thoughtcrime.securesms.profiles.manage;
|
package org.thoughtcrime.securesms.profiles.manage;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -17,6 +17,7 @@ import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.navigation.Navigation;
|
import androidx.navigation.Navigation;
|
||||||
|
@ -29,10 +30,12 @@ import com.google.android.material.textfield.TextInputLayout;
|
||||||
|
|
||||||
import org.signal.core.util.DimensionUnit;
|
import org.signal.core.util.DimensionUnit;
|
||||||
import org.thoughtcrime.securesms.LoggingFragment;
|
import org.thoughtcrime.securesms.LoggingFragment;
|
||||||
|
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
|
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
|
||||||
import org.thoughtcrime.securesms.databinding.UsernameEditFragmentBinding;
|
import org.thoughtcrime.securesms.databinding.UsernameEditFragmentBinding;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.util.AccessibilityUtil;
|
||||||
import org.thoughtcrime.securesms.util.FragmentResultContract;
|
import org.thoughtcrime.securesms.util.FragmentResultContract;
|
||||||
import org.thoughtcrime.securesms.util.LifecycleDisposable;
|
import org.thoughtcrime.securesms.util.LifecycleDisposable;
|
||||||
import org.thoughtcrime.securesms.util.UsernameUtil;
|
import org.thoughtcrime.securesms.util.UsernameUtil;
|
||||||
|
@ -49,6 +52,7 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||||
private UsernameEditFragmentBinding binding;
|
private UsernameEditFragmentBinding binding;
|
||||||
private ImageView suffixProgress;
|
private ImageView suffixProgress;
|
||||||
private LifecycleDisposable lifecycleDisposable;
|
private LifecycleDisposable lifecycleDisposable;
|
||||||
|
private UsernameEditFragmentArgs args;
|
||||||
|
|
||||||
public static UsernameEditFragment newInstance() {
|
public static UsernameEditFragment newInstance() {
|
||||||
return new UsernameEditFragment();
|
return new UsernameEditFragment();
|
||||||
|
@ -62,20 +66,37 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
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);
|
binding.usernameTextWrapper.setErrorIconDrawable(null);
|
||||||
|
|
||||||
lifecycleDisposable = new LifecycleDisposable();
|
lifecycleDisposable = new LifecycleDisposable();
|
||||||
lifecycleDisposable.bindTo(getViewLifecycleOwner());
|
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));
|
lifecycleDisposable.add(viewModel.getUiState().subscribe(this::onUiStateChanged));
|
||||||
viewModel.getEvents().observe(getViewLifecycleOwner(), this::onEvent);
|
viewModel.getEvents().observe(getViewLifecycleOwner(), this::onEvent);
|
||||||
|
|
||||||
binding.usernameSubmitButton.setOnClickListener(v -> viewModel.onUsernameSubmitted());
|
binding.usernameSubmitButton.setOnClickListener(v -> viewModel.onUsernameSubmitted());
|
||||||
binding.usernameDeleteButton.setOnClickListener(v -> viewModel.onUsernameDeleted());
|
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);
|
UsernameState usernameState = Recipient.self().getUsername().<UsernameState>map(UsernameState.Set::new).orElse(UsernameState.NoUsername.INSTANCE);
|
||||||
binding.usernameText.setText(usernameState.getNickname());
|
binding.usernameText.setText(usernameState.getNickname());
|
||||||
|
@ -142,15 +163,82 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUiStateChanged(@NonNull UsernameEditViewModel.State state) {
|
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 submitButton = binding.usernameSubmitButton;
|
||||||
CircularProgressMaterialButton deleteButton = binding.usernameDeleteButton;
|
CircularProgressMaterialButton deleteButton = binding.usernameDeleteButton;
|
||||||
TextInputLayout usernameInputWrapper = binding.usernameTextWrapper;
|
EditText usernameInput = binding.usernameText;
|
||||||
|
|
||||||
usernameInput.setEnabled(true);
|
usernameInput.setEnabled(true);
|
||||||
presentSuffix(state.getUsername());
|
switch (buttonState) {
|
||||||
|
|
||||||
switch (state.getButtonState()) {
|
|
||||||
case SUBMIT:
|
case SUBMIT:
|
||||||
submitButton.cancelSpinning();
|
submitButton.cancelSpinning();
|
||||||
submitButton.setVisibility(View.VISIBLE);
|
submitButton.setVisibility(View.VISIBLE);
|
||||||
|
@ -194,38 +282,6 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||||
usernameInput.setEnabled(false);
|
usernameInput.setEnabled(false);
|
||||||
break;
|
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) {
|
private void presentSuffix(@NonNull UsernameState usernameState) {
|
||||||
|
@ -257,7 +313,7 @@ public class UsernameEditFragment extends LoggingFragment {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case SUBMIT_SUCCESS:
|
case SUBMIT_SUCCESS:
|
||||||
ResultContract.setUsernameCreated(getParentFragmentManager());
|
ResultContract.setUsernameCreated(getParentFragmentManager());
|
||||||
NavHostFragment.findNavController(this).popBackStack();
|
closeScreen();
|
||||||
break;
|
break;
|
||||||
case SUBMIT_FAIL_TAKEN:
|
case SUBMIT_FAIL_TAKEN:
|
||||||
Toast.makeText(requireContext(), R.string.UsernameEditFragment_this_username_is_taken, Toast.LENGTH_SHORT).show();
|
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:
|
case NETWORK_FAILURE:
|
||||||
Toast.makeText(requireContext(), R.string.UsernameEditFragment_encountered_a_network_error, Toast.LENGTH_SHORT).show();
|
Toast.makeText(requireContext(), R.string.UsernameEditFragment_encountered_a_network_error, Toast.LENGTH_SHORT).show();
|
||||||
break;
|
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 androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import org.signal.core.util.ThreadUtil;
|
import org.signal.core.util.ThreadUtil;
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||||
import org.thoughtcrime.securesms.util.UsernameUtil;
|
import org.thoughtcrime.securesms.util.UsernameUtil;
|
||||||
|
@ -45,13 +46,15 @@ class UsernameEditViewModel extends ViewModel {
|
||||||
private final RxStore<State> uiState;
|
private final RxStore<State> uiState;
|
||||||
private final PublishProcessor<String> nicknamePublisher;
|
private final PublishProcessor<String> nicknamePublisher;
|
||||||
private final CompositeDisposable disposables;
|
private final CompositeDisposable disposables;
|
||||||
|
private final boolean isInRegistration;
|
||||||
|
|
||||||
private UsernameEditViewModel() {
|
private UsernameEditViewModel(boolean isInRegistration) {
|
||||||
this.repo = new UsernameEditRepository();
|
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.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.events = new SingleLiveEvent<>();
|
||||||
this.nicknamePublisher = PublishProcessor.create();
|
this.nicknamePublisher = PublishProcessor.create();
|
||||||
this.disposables = new CompositeDisposable();
|
this.disposables = new CompositeDisposable();
|
||||||
|
this.isInRegistration = isInRegistration;
|
||||||
|
|
||||||
Disposable disposable = nicknamePublisher.debounce(NICKNAME_PUBLISHER_DEBOUNCE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
|
Disposable disposable = nicknamePublisher.debounce(NICKNAME_PUBLISHER_DEBOUNCE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
|
||||||
.subscribe(this::onNicknameChanged);
|
.subscribe(this::onNicknameChanged);
|
||||||
|
@ -67,7 +70,7 @@ class UsernameEditViewModel extends ViewModel {
|
||||||
void onNicknameUpdated(@NonNull String nickname) {
|
void onNicknameUpdated(@NonNull String nickname) {
|
||||||
uiState.update(state -> {
|
uiState.update(state -> {
|
||||||
if (TextUtils.isEmpty(nickname) && Recipient.self().getUsername().isPresent()) {
|
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);
|
Optional<InvalidReason> invalidReason = UsernameUtil.checkUsername(nickname);
|
||||||
|
@ -79,6 +82,11 @@ class UsernameEditViewModel extends ViewModel {
|
||||||
nicknamePublisher.onNext(nickname);
|
nicknamePublisher.onNext(nickname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onUsernameSkipped() {
|
||||||
|
SignalStore.uiHints().markHasSetOrSkippedUsernameCreation();
|
||||||
|
events.setValue(Event.SKIPPED);
|
||||||
|
}
|
||||||
|
|
||||||
void onUsernameSubmitted() {
|
void onUsernameSubmitted() {
|
||||||
UsernameState usernameState = uiState.getState().getUsername();
|
UsernameState usernameState = uiState.getState().getUsername();
|
||||||
|
|
||||||
|
@ -107,6 +115,7 @@ class UsernameEditViewModel extends ViewModel {
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case SUCCESS:
|
case SUCCESS:
|
||||||
|
SignalStore.uiHints().markHasSetOrSkippedUsernameCreation();
|
||||||
uiState.update(state -> new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, state.usernameState));
|
uiState.update(state -> new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, state.usernameState));
|
||||||
events.postValue(Event.SUBMIT_SUCCESS);
|
events.postValue(Event.SUBMIT_SUCCESS);
|
||||||
break;
|
break;
|
||||||
|
@ -248,14 +257,21 @@ class UsernameEditViewModel extends ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Event {
|
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 {
|
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||||
|
|
||||||
|
private final boolean isInRegistration;
|
||||||
|
|
||||||
|
Factory(boolean isInRegistration) {
|
||||||
|
this.isInRegistration = isInRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||||
//noinspection ConstantConditions
|
//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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:parentTag="com.google.android.material.card.MaterialCardView"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||||
|
android:id="@+id/progress_card_child"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_margin="24dp"
|
||||||
|
android:indeterminate="true"
|
||||||
|
app:indicatorColor="@color/signal_colorPrimary" />
|
||||||
|
</merge>
|
|
@ -98,14 +98,45 @@
|
||||||
app:layout_constraintTop_toBottomOf="@id/username_text_wrapper"
|
app:layout_constraintTop_toBottomOf="@id/username_text_wrapper"
|
||||||
app:layout_constraintVertical_bias="0" />
|
app:layout_constraintVertical_bias="0" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/username_skip_button"
|
||||||
|
style="@style/Widget.Signal.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:minWidth="48dp"
|
||||||
|
android:text="@string/UsernameEditFragment__skip"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/username_done_button"
|
||||||
|
style="@style/ThemeOverlay.Material3.FloatingActionButton.Primary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:contentDescription="@string/UsernameEditFragment__done"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:backgroundTint="@color/signal_colorPrimaryContainer"
|
||||||
|
app:fabCustomSize="48dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_arrow_end_24"
|
||||||
|
app:tint="@color/signal_colorOnPrimaryContainer"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton
|
<org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton
|
||||||
android:id="@+id/username_submit_button"
|
android:id="@+id/username_submit_button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="32dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
app:circularProgressMaterialButton__label="@string/save"
|
app:circularProgressMaterialButton__label="@string/save"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
@ -115,7 +146,6 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="32dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
@ -123,11 +153,23 @@
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.components.ProgressCard
|
||||||
|
android:id="@+id/username_progress_card"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:cardCornerRadius="18dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Barrier
|
<androidx.constraintlayout.widget.Barrier
|
||||||
android:id="@+id/username_button_barrier"
|
android:id="@+id/username_button_barrier"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:barrierDirection="top"
|
app:barrierDirection="top"
|
||||||
app:constraint_referenced_ids="username_submit_button,username_delete_button" />
|
app:constraint_referenced_ids="username_submit_button,username_delete_button,username_skip_button,username_done_button" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/create_username"
|
||||||
|
app:startDestination="@id/createUsernameFragment">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/createUsernameFragment"
|
||||||
|
android:name="org.thoughtcrime.securesms.profiles.manage.UsernameEditFragment"
|
||||||
|
tools:layout="@layout/username_edit_fragment">
|
||||||
|
|
||||||
|
<argument
|
||||||
|
android:name="is_in_registration"
|
||||||
|
android:defaultValue="false"
|
||||||
|
app:argType="boolean" />
|
||||||
|
|
||||||
|
</fragment>
|
||||||
|
|
||||||
|
</navigation>
|
|
@ -11,14 +11,6 @@
|
||||||
android:label="fragment_create_profile"
|
android:label="fragment_create_profile"
|
||||||
tools:layout="@layout/profile_create_fragment">
|
tools:layout="@layout/profile_create_fragment">
|
||||||
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_editUsername"
|
|
||||||
app:destination="@id/usernameEditFragment"
|
|
||||||
app:enterAnim="@anim/nav_default_enter_anim"
|
|
||||||
app:exitAnim="@anim/nav_default_exit_anim"
|
|
||||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
|
||||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
|
||||||
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_createProfileFragment_to_avatar_picker"
|
android:id="@+id/action_createProfileFragment_to_avatar_picker"
|
||||||
app:destination="@id/avatar_picker"
|
app:destination="@id/avatar_picker"
|
||||||
|
@ -47,12 +39,6 @@
|
||||||
|
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/usernameEditFragment"
|
|
||||||
android:name="org.thoughtcrime.securesms.profiles.manage.UsernameEditFragment"
|
|
||||||
android:label="fragment_edit_username"
|
|
||||||
tools:layout="@layout/username_edit_fragment" />
|
|
||||||
|
|
||||||
<include app:graph="@navigation/avatar_picker" />
|
<include app:graph="@navigation/avatar_picker" />
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
|
|
|
@ -1873,6 +1873,8 @@
|
||||||
<string name="UnverifiedSendDialog_send">Send</string>
|
<string name="UnverifiedSendDialog_send">Send</string>
|
||||||
|
|
||||||
<!-- UsernameEditFragment -->
|
<!-- UsernameEditFragment -->
|
||||||
|
<!-- Toolbar title when entering from registration -->
|
||||||
|
<string name="UsernameEditFragment__add_a_username">Add a username</string>
|
||||||
<!-- Instructional text at the top of the username edit screen -->
|
<!-- Instructional text at the top of the username edit screen -->
|
||||||
<string name="UsernameEditFragment__choose_your_username">Choose your username</string>
|
<string name="UsernameEditFragment__choose_your_username">Choose your username</string>
|
||||||
<string name="UsernameEditFragment_username">Username</string>
|
<string name="UsernameEditFragment_username">Username</string>
|
||||||
|
@ -1891,6 +1893,10 @@
|
||||||
<!-- Dialog title for explanation about numbers at the end of the username -->
|
<!-- Dialog title for explanation about numbers at the end of the username -->
|
||||||
<string name="UsernameEditFragment__what_is_this_number">What is this number?</string>
|
<string name="UsernameEditFragment__what_is_this_number">What is this number?</string>
|
||||||
<string name="UsernameEditFragment__these_digits_help_keep">These digits help keep your username private so you can avoid unwanted messages. Share your username with only the people and groups you\'d like to chat with. If you change usernames you\'ll get a new set of digits.</string>
|
<string name="UsernameEditFragment__these_digits_help_keep">These digits help keep your username private so you can avoid unwanted messages. Share your username with only the people and groups you\'d like to chat with. If you change usernames you\'ll get a new set of digits.</string>
|
||||||
|
<!-- Button to allow user to skip -->
|
||||||
|
<string name="UsernameEditFragment__skip">Skip</string>
|
||||||
|
<!-- Content description for done button -->
|
||||||
|
<string name="UsernameEditFragment__done">Done</string>
|
||||||
|
|
||||||
<plurals name="UserNotificationMigrationJob_d_contacts_are_on_signal">
|
<plurals name="UserNotificationMigrationJob_d_contacts_are_on_signal">
|
||||||
<item quantity="one">%d contact is on Signal!</item>
|
<item quantity="one">%d contact is on Signal!</item>
|
||||||
|
|
Ładowanie…
Reference in New Issue