kopia lustrzana https://github.com/ryukoposting/Signal-Android
Username UX refresh.
rodzic
3252871ed5
commit
28310a88f5
|
@ -12,14 +12,12 @@ import android.view.View;
|
|||
import android.view.ViewAnimationUtils;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
|
@ -28,11 +26,13 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
|||
|
||||
import org.signal.core.util.EditTextUtil;
|
||||
import org.signal.core.util.StreamUtil;
|
||||
import org.signal.core.util.concurrent.SimpleTask;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.avatar.Avatars;
|
||||
import org.thoughtcrime.securesms.avatar.picker.AvatarPickerFragment;
|
||||
import org.thoughtcrime.securesms.databinding.ProfileCreateFragmentBinding;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.ParcelableGroupId;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
|
@ -41,11 +41,8 @@ import org.thoughtcrime.securesms.profiles.manage.EditProfileNameFragment;
|
|||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.signal.core.util.concurrent.SimpleTask;
|
||||
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
|
||||
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
|
||||
import org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton;
|
||||
import org.thoughtcrime.securesms.util.views.LearnMoreTextView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -62,20 +59,10 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
private static final int MAX_DESCRIPTION_GLYPHS = 480;
|
||||
private static final int MAX_DESCRIPTION_BYTES = 8192;
|
||||
|
||||
private Toolbar toolbar;
|
||||
private View title;
|
||||
private ImageView avatar;
|
||||
private CircularProgressMaterialButton finishButton;
|
||||
private EditText givenName;
|
||||
private EditText familyName;
|
||||
private View reveal;
|
||||
private TextView preview;
|
||||
private ImageView avatarPreviewBackground;
|
||||
private ImageView avatarPreview;
|
||||
|
||||
private Intent nextIntent;
|
||||
|
||||
private EditProfileViewModel viewModel;
|
||||
private EditProfileViewModel viewModel;
|
||||
private ProfileCreateFragmentBinding binding;
|
||||
|
||||
private Controller controller;
|
||||
|
||||
|
@ -92,7 +79,8 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
|
||||
@Override
|
||||
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.profile_create_fragment, container, false);
|
||||
binding = ProfileCreateFragmentBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -108,7 +96,7 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
if (bundle.getBoolean(AvatarPickerFragment.SELECT_AVATAR_CLEAR)) {
|
||||
viewModel.setAvatarMedia(null);
|
||||
viewModel.setAvatar(null);
|
||||
avatar.setImageDrawable(null);
|
||||
binding.avatar.setImageDrawable(null);
|
||||
} else {
|
||||
Media media = bundle.getParcelable(AvatarPickerFragment.SELECT_AVATAR_MEDIA);
|
||||
handleMediaFromResult(media);
|
||||
|
@ -116,6 +104,12 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
private void handleMediaFromResult(@NonNull Media media) {
|
||||
SimpleTask.run(() -> {
|
||||
try {
|
||||
|
@ -136,7 +130,7 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
.skipMemoryCache(true)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.circleCrop()
|
||||
.into(avatar);
|
||||
.into(binding.avatar);
|
||||
} else {
|
||||
Toast.makeText(requireActivity(), R.string.CreateProfileActivity_error_setting_profile_photo, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
@ -162,111 +156,108 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
Bundle arguments = requireArguments();
|
||||
boolean isEditingGroup = groupId != null;
|
||||
|
||||
this.toolbar = view.findViewById(R.id.toolbar);
|
||||
this.title = view.findViewById(R.id.title);
|
||||
this.avatar = view.findViewById(R.id.avatar);
|
||||
this.givenName = view.findViewById(R.id.given_name);
|
||||
this.familyName = view.findViewById(R.id.family_name);
|
||||
this.finishButton = view.findViewById(R.id.finish_button);
|
||||
this.reveal = view.findViewById(R.id.reveal);
|
||||
this.preview = view.findViewById(R.id.name_preview);
|
||||
this.avatarPreviewBackground = view.findViewById(R.id.avatar_background);
|
||||
this.avatarPreview = view.findViewById(R.id.avatar_placeholder);
|
||||
this.nextIntent = arguments.getParcelable(NEXT_INTENT);
|
||||
this.nextIntent = arguments.getParcelable(NEXT_INTENT);
|
||||
|
||||
this.avatar.setOnClickListener(v -> startAvatarSelection());
|
||||
|
||||
view.findViewById(R.id.mms_group_hint)
|
||||
.setVisibility(isEditingGroup && groupId.isMms() ? View.VISIBLE : View.GONE);
|
||||
binding.avatar.setOnClickListener(v -> startAvatarSelection());
|
||||
binding.mmsGroupHint.setVisibility(isEditingGroup && groupId.isMms() ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (isEditingGroup) {
|
||||
EditTextUtil.addGraphemeClusterLimitFilter(givenName, FeatureFlags.getMaxGroupNameGraphemeLength());
|
||||
givenName.addTextChangedListener(new AfterTextChanged(s -> viewModel.setGivenName(s.toString())));
|
||||
givenName.setHint(R.string.EditProfileFragment__group_name);
|
||||
givenName.requestFocus();
|
||||
toolbar.setTitle(R.string.EditProfileFragment__edit_group);
|
||||
preview.setVisibility(View.GONE);
|
||||
EditTextUtil.addGraphemeClusterLimitFilter(binding.givenName, FeatureFlags.getMaxGroupNameGraphemeLength());
|
||||
binding.profileDescriptionText.setVisibility(View.GONE);
|
||||
binding.whoCanFindMeContainer.setVisibility(View.GONE);
|
||||
binding.givenName.addTextChangedListener(new AfterTextChanged(s -> viewModel.setGivenName(s.toString())));
|
||||
binding.givenNameWrapper.setHint(R.string.EditProfileFragment__group_name);
|
||||
binding.givenName.requestFocus();
|
||||
binding.toolbar.setTitle(R.string.EditProfileFragment__edit_group);
|
||||
binding.namePreview.setVisibility(View.GONE);
|
||||
|
||||
if (groupId.isV2()) {
|
||||
EditTextUtil.addGraphemeClusterLimitFilter(familyName, MAX_DESCRIPTION_GLYPHS);
|
||||
familyName.addTextChangedListener(new AfterTextChanged(s -> {
|
||||
EditTextUtil.addGraphemeClusterLimitFilter(binding.familyName, MAX_DESCRIPTION_GLYPHS);
|
||||
binding.familyName.addTextChangedListener(new AfterTextChanged(s -> {
|
||||
EditProfileNameFragment.trimFieldToMaxByteLength(s, MAX_DESCRIPTION_BYTES);
|
||||
viewModel.setFamilyName(s.toString());
|
||||
}));
|
||||
familyName.setHint(R.string.EditProfileFragment__group_description);
|
||||
familyName.setSingleLine(false);
|
||||
familyName.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
|
||||
binding.familyNameWrapper.setHint(R.string.EditProfileFragment__group_description);
|
||||
binding.familyName.setSingleLine(false);
|
||||
binding.familyName.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
|
||||
|
||||
LearnMoreTextView descriptionText = view.findViewById(R.id.description_text);
|
||||
descriptionText.setLearnMoreVisible(false);
|
||||
descriptionText.setText(R.string.CreateProfileActivity_group_descriptions_will_be_visible_to_members_of_this_group_and_people_who_have_been_invited);
|
||||
binding.groupDescriptionText.setLearnMoreVisible(false);
|
||||
binding.groupDescriptionText.setText(R.string.CreateProfileActivity_group_descriptions_will_be_visible_to_members_of_this_group_and_people_who_have_been_invited);
|
||||
} else {
|
||||
familyName.setVisibility(View.GONE);
|
||||
familyName.setEnabled(false);
|
||||
view.findViewById(R.id.description_text).setVisibility(View.GONE);
|
||||
binding.familyNameWrapper.setVisibility(View.GONE);
|
||||
binding.familyName.setEnabled(false);
|
||||
binding.groupDescriptionText.setVisibility(View.GONE);
|
||||
}
|
||||
view.<ImageView>findViewById(R.id.avatar_placeholder).setImageResource(R.drawable.ic_group_outline_40);
|
||||
binding.avatarPlaceholder.setImageResource(R.drawable.ic_group_outline_40);
|
||||
} else {
|
||||
EditTextUtil.addGraphemeClusterLimitFilter(givenName, EditProfileNameFragment.NAME_MAX_GLYPHS);
|
||||
EditTextUtil.addGraphemeClusterLimitFilter(familyName, EditProfileNameFragment.NAME_MAX_GLYPHS);
|
||||
this.givenName.addTextChangedListener(new AfterTextChanged(s -> {
|
||||
EditTextUtil.addGraphemeClusterLimitFilter(binding.givenName, EditProfileNameFragment.NAME_MAX_GLYPHS);
|
||||
EditTextUtil.addGraphemeClusterLimitFilter(binding.familyName, EditProfileNameFragment.NAME_MAX_GLYPHS);
|
||||
binding.givenName.addTextChangedListener(new AfterTextChanged(s -> {
|
||||
EditProfileNameFragment.trimFieldToMaxByteLength(s);
|
||||
viewModel.setGivenName(s.toString());
|
||||
}));
|
||||
this.familyName.addTextChangedListener(new AfterTextChanged(s -> {
|
||||
binding.familyName.addTextChangedListener(new AfterTextChanged(s -> {
|
||||
EditProfileNameFragment.trimFieldToMaxByteLength(s);
|
||||
viewModel.setFamilyName(s.toString());
|
||||
}));
|
||||
LearnMoreTextView descriptionText = view.findViewById(R.id.description_text);
|
||||
descriptionText.setLearnMoreVisible(true);
|
||||
descriptionText.setOnLinkClickListener(v -> CommunicationActions.openBrowserLink(requireContext(), getString(R.string.EditProfileFragment__support_link)));
|
||||
binding.groupDescriptionText.setVisibility(View.GONE);
|
||||
binding.profileDescriptionText.setLearnMoreVisible(true);
|
||||
binding.profileDescriptionText.setLinkColor(ContextCompat.getColor(requireContext(), R.color.signal_colorPrimary));
|
||||
binding.profileDescriptionText.setOnLinkClickListener(v -> CommunicationActions.openBrowserLink(requireContext(), getString(R.string.EditProfileFragment__support_link)));
|
||||
|
||||
if (FeatureFlags.phoneNumberPrivacy()) {
|
||||
binding.whoCanFindMeContainer.setVisibility(View.VISIBLE);
|
||||
binding.whoCanFindMeContainer.setOnClickListener(v -> SafeNavigation.safeNavigate(Navigation.findNavController(v), EditProfileFragmentDirections.actionCreateProfileFragmentToPhoneNumberPrivacy()));
|
||||
// TODO [alex] -- Where does this value come from?
|
||||
binding.whoCanFindMeDescription.setText(R.string.PhoneNumberPrivacy_everyone);
|
||||
}
|
||||
}
|
||||
|
||||
this.finishButton.setOnClickListener(v -> {
|
||||
this.finishButton.setSpinning();
|
||||
binding.finishButton.setOnClickListener(v -> {
|
||||
binding.finishButton.setSpinning();
|
||||
handleUpload();
|
||||
});
|
||||
|
||||
this.finishButton.setText(arguments.getInt(NEXT_BUTTON_TEXT, R.string.CreateProfileActivity_next));
|
||||
binding.finishButton.setText(arguments.getInt(NEXT_BUTTON_TEXT, R.string.CreateProfileActivity_next));
|
||||
|
||||
if (arguments.getBoolean(SHOW_TOOLBAR, true)) {
|
||||
this.toolbar.setVisibility(View.VISIBLE);
|
||||
this.toolbar.setNavigationOnClickListener(v -> requireActivity().finish());
|
||||
this.title.setVisibility(View.GONE);
|
||||
binding.toolbar.setVisibility(View.VISIBLE);
|
||||
binding.toolbar.setNavigationOnClickListener(v -> requireActivity().finish());
|
||||
binding.title.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeProfileName() {
|
||||
viewModel.isFormValid().observe(getViewLifecycleOwner(), isValid -> {
|
||||
finishButton.setEnabled(isValid);
|
||||
finishButton.setAlpha(isValid ? 1f : 0.5f);
|
||||
binding.finishButton.setEnabled(isValid);
|
||||
binding.finishButton.setAlpha(isValid ? 1f : 0.5f);
|
||||
});
|
||||
|
||||
viewModel.givenName().observe(getViewLifecycleOwner(), givenName -> updateFieldIfNeeded(this.givenName, givenName));
|
||||
viewModel.givenName().observe(getViewLifecycleOwner(), givenName -> updateFieldIfNeeded(binding.givenName, givenName));
|
||||
|
||||
viewModel.familyName().observe(getViewLifecycleOwner(), familyName -> updateFieldIfNeeded(this.familyName, familyName));
|
||||
viewModel.familyName().observe(getViewLifecycleOwner(), familyName -> updateFieldIfNeeded(binding.familyName, familyName));
|
||||
|
||||
viewModel.profileName().observe(getViewLifecycleOwner(), profileName -> preview.setText(profileName.toString()));
|
||||
viewModel.profileName().observe(getViewLifecycleOwner(), profileName -> binding.namePreview.setText(profileName.toString()));
|
||||
}
|
||||
|
||||
private void initializeProfileAvatar() {
|
||||
viewModel.avatar().observe(getViewLifecycleOwner(), bytes -> {
|
||||
if (bytes == null) {
|
||||
GlideApp.with(this).clear(avatar);
|
||||
GlideApp.with(this).clear(binding.avatar);
|
||||
return;
|
||||
}
|
||||
|
||||
GlideApp.with(this)
|
||||
.load(bytes)
|
||||
.circleCrop()
|
||||
.into(avatar);
|
||||
.into(binding.avatar);
|
||||
});
|
||||
|
||||
viewModel.avatarColor().observe(getViewLifecycleOwner(), avatarColor -> {
|
||||
Avatars.ForegroundColor foregroundColor = Avatars.getForegroundColor(avatarColor);
|
||||
|
||||
avatarPreview.getDrawable().setColorFilter(new SimpleColorFilter(foregroundColor.getColorInt()));
|
||||
avatarPreviewBackground.getDrawable().setColorFilter(new SimpleColorFilter(avatarColor.colorInt()));
|
||||
binding.avatarPlaceholder.getDrawable().setColorFilter(new SimpleColorFilter(foregroundColor.getColorInt()));
|
||||
binding.avatarBackground.getDrawable().setColorFilter(new SimpleColorFilter(avatarColor.colorInt()));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -312,7 +303,7 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
}
|
||||
|
||||
private void handleFinishedLegacy() {
|
||||
finishButton.cancelSpinning();
|
||||
binding.finishButton.cancelSpinning();
|
||||
if (nextIntent != null) startActivity(nextIntent);
|
||||
|
||||
controller.onProfileNameUploadCompleted();
|
||||
|
@ -323,16 +314,16 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
int[] finishButtonLocation = new int[2];
|
||||
int[] revealLocation = new int[2];
|
||||
|
||||
finishButton.getLocationInWindow(finishButtonLocation);
|
||||
reveal.getLocationInWindow(revealLocation);
|
||||
binding.finishButton.getLocationInWindow(finishButtonLocation);
|
||||
binding.reveal.getLocationInWindow(revealLocation);
|
||||
|
||||
int finishX = finishButtonLocation[0] - revealLocation[0];
|
||||
int finishY = finishButtonLocation[1] - revealLocation[1];
|
||||
|
||||
finishX += finishButton.getWidth() / 2;
|
||||
finishY += finishButton.getHeight() / 2;
|
||||
finishX += binding.finishButton.getWidth() / 2;
|
||||
finishY += binding.finishButton.getHeight() / 2;
|
||||
|
||||
Animator animation = ViewAnimationUtils.createCircularReveal(reveal, finishX, finishY, 0f, (float) Math.max(reveal.getWidth(), reveal.getHeight()));
|
||||
Animator animation = ViewAnimationUtils.createCircularReveal(binding.reveal, finishX, finishY, 0f, (float) Math.max(binding.reveal.getWidth(), binding.reveal.getHeight()));
|
||||
animation.setDuration(500);
|
||||
animation.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
|
@ -340,7 +331,7 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
finishButton.cancelSpinning();
|
||||
binding.finishButton.cancelSpinning();
|
||||
if (nextIntent != null && getActivity() != null) {
|
||||
startActivity(nextIntent);
|
||||
}
|
||||
|
@ -355,7 +346,7 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
public void onAnimationRepeat(Animator animation) {}
|
||||
});
|
||||
|
||||
reveal.setVisibility(View.VISIBLE);
|
||||
binding.reveal.setVisibility(View.VISIBLE);
|
||||
animation.start();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package org.thoughtcrime.securesms.profiles.edit.pnp
|
||||
|
||||
import androidx.fragment.app.viewModels
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||
|
||||
/**
|
||||
* Allows the user to select who can see their phone number during registration.
|
||||
*/
|
||||
class WhoCanSeeMyPhoneNumberFragment : DSLSettingsFragment(titleId = R.string.WhoCanSeeMyPhoneNumberFragment__who_can_find_me_by_number) {
|
||||
|
||||
private val viewModel: WhoCanSeeMyPhoneNumberViewModel by viewModels()
|
||||
private val lifecycleDisposable = LifecycleDisposable()
|
||||
|
||||
override fun bindAdapter(adapter: DSLSettingsAdapter) {
|
||||
require(FeatureFlags.phoneNumberPrivacy())
|
||||
|
||||
lifecycleDisposable += viewModel.state.subscribe {
|
||||
adapter.submitList(getConfiguration(it).toMappingModelList())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConfiguration(state: WhoCanSeeMyPhoneNumberState): DSLConfiguration {
|
||||
return configure {
|
||||
radioPref(
|
||||
title = DSLSettingsText.from(R.string.PhoneNumberPrivacy_everyone),
|
||||
summary = DSLSettingsText.from(R.string.WhoCanSeeMyPhoneNumberFragment__anyone_who_has),
|
||||
isChecked = state == WhoCanSeeMyPhoneNumberState.EVERYONE,
|
||||
onClick = { viewModel.onEveryoneCanSeeMyPhoneNumberSelected() }
|
||||
)
|
||||
|
||||
radioPref(
|
||||
title = DSLSettingsText.from(R.string.PhoneNumberPrivacy_nobody),
|
||||
summary = DSLSettingsText.from(R.string.WhoCanSeeMyPhoneNumberFragment__nobody_on_signal),
|
||||
isChecked = state == WhoCanSeeMyPhoneNumberState.NOBODY,
|
||||
onClick = { viewModel.onNobodyCanSeeMyPhoneNumberSelected() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package org.thoughtcrime.securesms.profiles.edit.pnp
|
||||
|
||||
enum class WhoCanSeeMyPhoneNumberState {
|
||||
EVERYONE,
|
||||
NOBODY
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package org.thoughtcrime.securesms.profiles.edit.pnp
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import org.thoughtcrime.securesms.util.rx.RxStore
|
||||
|
||||
class WhoCanSeeMyPhoneNumberViewModel : ViewModel() {
|
||||
|
||||
private val store = RxStore(WhoCanSeeMyPhoneNumberState.EVERYONE)
|
||||
private val disposables = CompositeDisposable()
|
||||
|
||||
val state: Flowable<WhoCanSeeMyPhoneNumberState> = store.stateFlowable.subscribeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
fun onEveryoneCanSeeMyPhoneNumberSelected() {
|
||||
store.update { WhoCanSeeMyPhoneNumberState.EVERYONE }
|
||||
}
|
||||
|
||||
fun onNobodyCanSeeMyPhoneNumberSelected() {
|
||||
store.update { WhoCanSeeMyPhoneNumberState.NOBODY }
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
disposables.clear()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package org.thoughtcrime.securesms.profiles.manage
|
||||
|
||||
import org.thoughtcrime.securesms.databinding.CopyButtonBinding
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.BindingFactory
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.BindingViewHolder
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
|
||||
|
||||
/**
|
||||
* Outlined button that allows the user to copy a piece of data.
|
||||
*/
|
||||
object CopyButton {
|
||||
fun register(mappingAdapter: MappingAdapter) {
|
||||
mappingAdapter.registerFactory(Model::class.java, BindingFactory(::ViewHolder, CopyButtonBinding::inflate))
|
||||
}
|
||||
|
||||
class Model(
|
||||
val text: CharSequence,
|
||||
val onClick: (Model) -> Unit
|
||||
) : MappingModel<Model> {
|
||||
override fun areItemsTheSame(newItem: Model): Boolean = true
|
||||
|
||||
override fun areContentsTheSame(newItem: Model): Boolean = text == newItem.text
|
||||
}
|
||||
|
||||
private class ViewHolder(binding: CopyButtonBinding) : BindingViewHolder<Model, CopyButtonBinding>(binding) {
|
||||
override fun bind(model: Model) {
|
||||
binding.root.text = model.text
|
||||
binding.root.setOnClickListener { model.onClick(model) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,33 +7,31 @@ import android.util.TypedValue;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
import com.airbnb.lottie.SimpleColorFilter;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.AvatarPreviewActivity;
|
||||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.avatar.Avatars;
|
||||
import org.thoughtcrime.securesms.avatar.picker.AvatarPickerFragment;
|
||||
import org.thoughtcrime.securesms.badges.BadgeImageView;
|
||||
import org.thoughtcrime.securesms.badges.models.Badge;
|
||||
import org.thoughtcrime.securesms.badges.self.none.BecomeASustainerFragment;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
|
||||
import org.thoughtcrime.securesms.databinding.ManageProfileFragmentBinding;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||
import org.thoughtcrime.securesms.profiles.manage.ManageProfileViewModel.AvatarState;
|
||||
|
@ -49,64 +47,42 @@ import java.util.Optional;
|
|||
|
||||
public class ManageProfileFragment extends LoggingFragment {
|
||||
|
||||
private static final String TAG = Log.tag(ManageProfileFragment.class);
|
||||
|
||||
private Toolbar toolbar;
|
||||
private ImageView avatarView;
|
||||
private ImageView avatarPlaceholderView;
|
||||
private TextView profileNameView;
|
||||
private View profileNameContainer;
|
||||
private TextView usernameView;
|
||||
private View usernameContainer;
|
||||
private TextView aboutView;
|
||||
private View aboutContainer;
|
||||
private ImageView aboutEmojiView;
|
||||
private AlertDialog avatarProgress;
|
||||
private TextView avatarInitials;
|
||||
private ImageView avatarBackground;
|
||||
private View badgesContainer;
|
||||
private BadgeImageView badgeView;
|
||||
|
||||
private ManageProfileViewModel viewModel;
|
||||
private AlertDialog avatarProgress;
|
||||
private ManageProfileViewModel viewModel;
|
||||
private ManageProfileFragmentBinding binding;
|
||||
|
||||
@Override
|
||||
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.manage_profile_fragment, container, false);
|
||||
binding = ManageProfileFragmentBinding.inflate(inflater, container, false);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
this.toolbar = view.findViewById(R.id.toolbar);
|
||||
this.avatarView = view.findViewById(R.id.manage_profile_avatar);
|
||||
this.avatarPlaceholderView = view.findViewById(R.id.manage_profile_avatar_placeholder);
|
||||
this.profileNameView = view.findViewById(R.id.manage_profile_name);
|
||||
this.profileNameContainer = view.findViewById(R.id.manage_profile_name_container);
|
||||
this.usernameView = view.findViewById(R.id.manage_profile_username);
|
||||
this.usernameContainer = view.findViewById(R.id.manage_profile_username_container);
|
||||
this.aboutView = view.findViewById(R.id.manage_profile_about);
|
||||
this.aboutContainer = view.findViewById(R.id.manage_profile_about_container);
|
||||
this.aboutEmojiView = view.findViewById(R.id.manage_profile_about_icon);
|
||||
this.avatarInitials = view.findViewById(R.id.manage_profile_avatar_initials);
|
||||
this.avatarBackground = view.findViewById(R.id.manage_profile_avatar_background);
|
||||
this.badgesContainer = view.findViewById(R.id.manage_profile_badges_container);
|
||||
this.badgeView = view.findViewById(R.id.manage_profile_badge);
|
||||
new UsernameEditFragment.ResultContract().registerForResult(getParentFragmentManager(), getViewLifecycleOwner(), isUsernameCreated -> {
|
||||
Snackbar.make(view, R.string.ManageProfileFragment__username_created, Snackbar.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
UsernameShareBottomSheet.ResultContract.INSTANCE.registerForResult(getParentFragmentManager(), getViewLifecycleOwner(), isCopiedToClipboard -> {
|
||||
Snackbar.make(view, R.string.ManageProfileFragment__username_copied, Snackbar.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
initializeViewModel();
|
||||
|
||||
this.toolbar.setNavigationOnClickListener(v -> requireActivity().finish());
|
||||
binding.toolbar.setNavigationOnClickListener(v -> requireActivity().finish());
|
||||
|
||||
View editAvatar = view.findViewById(R.id.manage_profile_edit_photo);
|
||||
editAvatar.setOnClickListener(v -> onEditAvatarClicked());
|
||||
binding.manageProfileEditPhoto.setOnClickListener(v -> onEditAvatarClicked());
|
||||
|
||||
this.profileNameContainer.setOnClickListener(v -> {
|
||||
binding.manageProfileNameContainer.setOnClickListener(v -> {
|
||||
SafeNavigation.safeNavigate(Navigation.findNavController(v), ManageProfileFragmentDirections.actionManageProfileName());
|
||||
});
|
||||
|
||||
this.usernameContainer.setOnClickListener(v -> {
|
||||
binding.manageProfileUsernameContainer.setOnClickListener(v -> {
|
||||
SafeNavigation.safeNavigate(Navigation.findNavController(v), ManageProfileFragmentDirections.actionManageUsername());
|
||||
});
|
||||
|
||||
this.aboutContainer.setOnClickListener(v -> {
|
||||
binding.manageProfileAboutContainer.setOnClickListener(v -> {
|
||||
SafeNavigation.safeNavigate(Navigation.findNavController(v), ManageProfileFragmentDirections.actionManageAbout());
|
||||
});
|
||||
|
||||
|
@ -119,6 +95,7 @@ public class ManageProfileFragment extends LoggingFragment {
|
|||
}
|
||||
});
|
||||
|
||||
EmojiTextView avatarInitials = binding.manageProfileAvatarInitials;
|
||||
avatarInitials.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
|
||||
if (avatarInitials.length() > 0) {
|
||||
updateInitials(avatarInitials.getText().toString());
|
||||
|
@ -126,7 +103,7 @@ public class ManageProfileFragment extends LoggingFragment {
|
|||
});
|
||||
|
||||
if (FeatureFlags.donorBadges()) {
|
||||
badgesContainer.setOnClickListener(v -> {
|
||||
binding.manageProfileBadgesContainer.setOnClickListener(v -> {
|
||||
if (Recipient.self().getBadges().isEmpty()) {
|
||||
BecomeASustainerFragment.show(getParentFragmentManager());
|
||||
} else {
|
||||
|
@ -134,17 +111,27 @@ public class ManageProfileFragment extends LoggingFragment {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
badgesContainer.setVisibility(View.GONE);
|
||||
binding.manageProfileBadgesContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
avatarView.setOnClickListener(v -> {
|
||||
binding.manageProfileAvatar.setOnClickListener(v -> {
|
||||
startActivity(AvatarPreviewActivity.intentFromRecipientId(requireContext(), Recipient.self().getId()),
|
||||
AvatarPreviewActivity.createTransitionBundle(requireActivity(), avatarView));
|
||||
AvatarPreviewActivity.createTransitionBundle(requireActivity(), binding.manageProfileAvatar));
|
||||
});
|
||||
|
||||
binding.manageProfileUsernameShare.setOnClickListener(v -> {
|
||||
SafeNavigation.safeNavigate(Navigation.findNavController(v), ManageProfileFragmentDirections.actionManageProfileFragmentToShareUsernameDialog());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
private void initializeViewModel() {
|
||||
viewModel = ViewModelProviders.of(this, new ManageProfileViewModel.Factory()).get(ManageProfileViewModel.class);
|
||||
viewModel = new ViewModelProvider(this, new ManageProfileViewModel.Factory()).get(ManageProfileViewModel.class);
|
||||
|
||||
LiveData<Optional<byte[]>> avatarImage = Transformations.map(LiveDataUtil.distinctUntilChanged(viewModel.getAvatar(), (b1, b2) -> Arrays.equals(b1.getAvatar(), b2.getAvatar())),
|
||||
b -> Optional.ofNullable(b.getAvatar()));
|
||||
|
@ -160,7 +147,7 @@ public class ManageProfileFragment extends LoggingFragment {
|
|||
if (viewModel.shouldShowUsername()) {
|
||||
viewModel.getUsername().observe(getViewLifecycleOwner(), this::presentUsername);
|
||||
} else {
|
||||
usernameContainer.setVisibility(View.GONE);
|
||||
binding.manageProfileUsernameContainer.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,9 +156,9 @@ public class ManageProfileFragment extends LoggingFragment {
|
|||
Glide.with(this)
|
||||
.load(avatarData.get())
|
||||
.circleCrop()
|
||||
.into(avatarView);
|
||||
.into(binding.manageProfileAvatar);
|
||||
} else {
|
||||
Glide.with(this).load((Drawable) null).into(avatarView);
|
||||
Glide.with(this).load((Drawable) null).into(binding.manageProfileAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,21 +167,21 @@ public class ManageProfileFragment extends LoggingFragment {
|
|||
CharSequence initials = NameUtil.getAbbreviation(avatarState.getSelf().getDisplayName(requireContext()));
|
||||
Avatars.ForegroundColor foregroundColor = Avatars.getForegroundColor(avatarState.getSelf().getAvatarColor());
|
||||
|
||||
avatarBackground.setColorFilter(new SimpleColorFilter(avatarState.getSelf().getAvatarColor().colorInt()));
|
||||
avatarPlaceholderView.setColorFilter(new SimpleColorFilter(foregroundColor.getColorInt()));
|
||||
avatarInitials.setTextColor(foregroundColor.getColorInt());
|
||||
binding.manageProfileAvatarBackground.setColorFilter(new SimpleColorFilter(avatarState.getSelf().getAvatarColor().colorInt()));
|
||||
binding.manageProfileAvatarPlaceholder.setColorFilter(new SimpleColorFilter(foregroundColor.getColorInt()));
|
||||
binding.manageProfileAvatarInitials.setTextColor(foregroundColor.getColorInt());
|
||||
|
||||
if (TextUtils.isEmpty(initials)) {
|
||||
avatarPlaceholderView.setVisibility(View.VISIBLE);
|
||||
avatarInitials.setVisibility(View.GONE);
|
||||
binding.manageProfileAvatarPlaceholder.setVisibility(View.VISIBLE);
|
||||
binding.manageProfileAvatarInitials.setVisibility(View.GONE);
|
||||
} else {
|
||||
updateInitials(initials.toString());
|
||||
avatarPlaceholderView.setVisibility(View.GONE);
|
||||
avatarInitials.setVisibility(View.VISIBLE);
|
||||
binding.manageProfileAvatarPlaceholder.setVisibility(View.GONE);
|
||||
binding.manageProfileAvatarInitials.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
avatarPlaceholderView.setVisibility(View.GONE);
|
||||
avatarInitials.setVisibility(View.GONE);
|
||||
binding.manageProfileAvatarPlaceholder.setVisibility(View.GONE);
|
||||
binding.manageProfileAvatarInitials.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (avatarProgress == null && avatarState.getLoadingState() == ManageProfileViewModel.LoadingState.LOADING) {
|
||||
|
@ -205,53 +192,59 @@ public class ManageProfileFragment extends LoggingFragment {
|
|||
}
|
||||
|
||||
private void updateInitials(String initials) {
|
||||
avatarInitials.setTextSize(TypedValue.COMPLEX_UNIT_PX, Avatars.getTextSizeForLength(requireContext(), initials, avatarInitials.getMeasuredWidth() * 0.8f, avatarInitials.getMeasuredWidth() * 0.45f));
|
||||
avatarInitials.setText(initials);
|
||||
binding.manageProfileAvatarInitials.setTextSize(TypedValue.COMPLEX_UNIT_PX,
|
||||
Avatars.getTextSizeForLength(requireContext(),
|
||||
initials,
|
||||
binding.manageProfileAvatarInitials.getMeasuredWidth() * 0.8f,
|
||||
binding.manageProfileAvatarInitials.getMeasuredWidth() * 0.45f));
|
||||
binding.manageProfileAvatarInitials.setText(initials);
|
||||
}
|
||||
|
||||
private void presentProfileName(@Nullable ProfileName profileName) {
|
||||
if (profileName == null || profileName.isEmpty()) {
|
||||
profileNameView.setText(R.string.ManageProfileFragment_profile_name);
|
||||
binding.manageProfileName.setText(R.string.ManageProfileFragment_profile_name);
|
||||
} else {
|
||||
profileNameView.setText(profileName.toString());
|
||||
binding.manageProfileName.setText(profileName.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void presentUsername(@Nullable String username) {
|
||||
if (username == null || username.isEmpty()) {
|
||||
usernameView.setText(R.string.ManageProfileFragment_username);
|
||||
binding.manageProfileUsername.setText(R.string.ManageProfileFragment_username);
|
||||
binding.manageProfileUsernameShare.setVisibility(View.GONE);
|
||||
} else {
|
||||
usernameView.setText(username);
|
||||
binding.manageProfileUsername.setText(username);
|
||||
binding.manageProfileUsernameShare.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void presentAbout(@Nullable String about) {
|
||||
if (about == null || about.isEmpty()) {
|
||||
aboutView.setText(R.string.ManageProfileFragment_about);
|
||||
binding.manageProfileAbout.setText(R.string.ManageProfileFragment_about);
|
||||
} else {
|
||||
aboutView.setText(about);
|
||||
binding.manageProfileAbout.setText(about);
|
||||
}
|
||||
}
|
||||
|
||||
private void presentAboutEmoji(@NonNull String aboutEmoji) {
|
||||
if (aboutEmoji == null || aboutEmoji.isEmpty()) {
|
||||
aboutEmojiView.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_compose_24, null));
|
||||
binding.manageProfileAboutIcon.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_compose_24, null));
|
||||
} else {
|
||||
Drawable emoji = EmojiUtil.convertToDrawable(requireContext(), aboutEmoji);
|
||||
|
||||
if (emoji != null) {
|
||||
aboutEmojiView.setImageDrawable(emoji);
|
||||
binding.manageProfileAboutIcon.setImageDrawable(emoji);
|
||||
} else {
|
||||
aboutEmojiView.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_compose_24, null));
|
||||
binding.manageProfileAboutIcon.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_compose_24, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void presentBadge(@NonNull Optional<Badge> badge) {
|
||||
if (badge.isPresent() && badge.get().getVisible() && !badge.get().isExpired()) {
|
||||
badgeView.setBadge(badge.orElse(null));
|
||||
binding.manageProfileBadge.setBadge(badge.orElse(null));
|
||||
} else {
|
||||
badgeView.setBadge(null);
|
||||
binding.manageProfileBadge.setBadge(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package org.thoughtcrime.securesms.profiles.manage
|
||||
|
||||
import org.thoughtcrime.securesms.databinding.ShareButtonBinding
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.BindingFactory
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.BindingViewHolder
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
|
||||
|
||||
object ShareButton {
|
||||
fun register(adapter: MappingAdapter) {
|
||||
adapter.registerFactory(Model::class.java, BindingFactory(::ViewHolder, ShareButtonBinding::inflate))
|
||||
}
|
||||
|
||||
class Model(
|
||||
val text: CharSequence,
|
||||
val onClick: (Model) -> Unit
|
||||
) : MappingModel<Model> {
|
||||
override fun areItemsTheSame(newItem: Model): Boolean = true
|
||||
|
||||
override fun areContentsTheSame(newItem: Model): Boolean = text == newItem.text
|
||||
}
|
||||
|
||||
private class ViewHolder(binding: ShareButtonBinding) : BindingViewHolder<Model, ShareButtonBinding>(binding) {
|
||||
override fun bind(model: Model) {
|
||||
binding.shareButton.setOnClickListener { model.onClick(model) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +1,56 @@
|
|||
package org.thoughtcrime.securesms.profiles.manage;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
|
||||
import org.signal.core.util.DimensionUnit;
|
||||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
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.FragmentResultContract;
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable;
|
||||
import org.thoughtcrime.securesms.util.UsernameUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton;
|
||||
import org.thoughtcrime.securesms.util.views.LearnMoreTextView;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class UsernameEditFragment extends LoggingFragment {
|
||||
|
||||
private static final float DISABLED_ALPHA = 0.5f;
|
||||
|
||||
private UsernameEditViewModel viewModel;
|
||||
|
||||
private EditText usernameInput;
|
||||
private TextView usernameSubtext;
|
||||
private CircularProgressMaterialButton submitButton;
|
||||
private CircularProgressMaterialButton deleteButton;
|
||||
private UsernameEditViewModel viewModel;
|
||||
private UsernameEditFragmentBinding binding;
|
||||
private ImageView suffixProgress;
|
||||
private LifecycleDisposable lifecycleDisposable;
|
||||
|
||||
public static UsernameEditFragment newInstance() {
|
||||
return new UsernameEditFragment();
|
||||
|
@ -40,46 +58,97 @@ public class UsernameEditFragment extends LoggingFragment {
|
|||
|
||||
@Override
|
||||
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.username_edit_fragment, container, false);
|
||||
binding = UsernameEditFragmentBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
usernameInput = view.findViewById(R.id.username_text);
|
||||
usernameSubtext = view.findViewById(R.id.username_subtext);
|
||||
submitButton = view.findViewById(R.id.username_submit_button);
|
||||
deleteButton = view.findViewById(R.id.username_delete_button);
|
||||
binding.toolbar.setNavigationOnClickListener(v -> Navigation.findNavController(view).popBackStack());
|
||||
|
||||
view.<Toolbar>findViewById(R.id.toolbar)
|
||||
.setNavigationOnClickListener(v -> Navigation.findNavController(view)
|
||||
.popBackStack());
|
||||
binding.usernameTextWrapper.setErrorIconDrawable(null);
|
||||
|
||||
viewModel = ViewModelProviders.of(this, new UsernameEditViewModel.Factory()).get(UsernameEditViewModel.class);
|
||||
lifecycleDisposable = new LifecycleDisposable();
|
||||
lifecycleDisposable.bindTo(getViewLifecycleOwner());
|
||||
|
||||
viewModel.getUiState().observe(getViewLifecycleOwner(), this::onUiStateChanged);
|
||||
viewModel = new ViewModelProvider(this, new UsernameEditViewModel.Factory()).get(UsernameEditViewModel.class);
|
||||
|
||||
lifecycleDisposable.add(viewModel.getUiState().subscribe(this::onUiStateChanged));
|
||||
viewModel.getEvents().observe(getViewLifecycleOwner(), this::onEvent);
|
||||
|
||||
submitButton.setOnClickListener(v -> viewModel.onUsernameSubmitted(usernameInput.getText().toString()));
|
||||
deleteButton.setOnClickListener(v -> viewModel.onUsernameDeleted());
|
||||
binding.usernameSubmitButton.setOnClickListener(v -> viewModel.onUsernameSubmitted(binding.usernameText.getText().toString()));
|
||||
binding.usernameDeleteButton.setOnClickListener(v -> viewModel.onUsernameDeleted());
|
||||
|
||||
usernameInput.setText(Recipient.self().getUsername().orElse(null));
|
||||
usernameInput.addTextChangedListener(new SimpleTextWatcher() {
|
||||
binding.usernameText.setText(Recipient.self().getUsername().orElse(null));
|
||||
binding.usernameText.addTextChangedListener(new SimpleTextWatcher() {
|
||||
@Override
|
||||
public void onTextChanged(String text) {
|
||||
viewModel.onUsernameUpdated(text);
|
||||
}
|
||||
});
|
||||
usernameInput.setOnEditorActionListener((v, actionId, event) -> {
|
||||
binding.usernameText.setOnEditorActionListener((v, actionId, event) -> {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
viewModel.onUsernameSubmitted(usernameInput.getText().toString());
|
||||
viewModel.onUsernameSubmitted(binding.usernameText.getText().toString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
binding.usernameDescription.setLinkColor(ContextCompat.getColor(requireContext(), R.color.signal_colorPrimary));
|
||||
binding.usernameDescription.setLearnMoreVisible(true);
|
||||
binding.usernameDescription.setOnLinkClickListener(this::onLearnMore);
|
||||
|
||||
initializeSuffix();
|
||||
ViewUtil.focusAndShowKeyboard(binding.usernameText);
|
||||
}
|
||||
|
||||
private void initializeSuffix() {
|
||||
TextView suffixTextView = binding.usernameTextWrapper.getSuffixTextView();
|
||||
Drawable pipe = Objects.requireNonNull(ContextCompat.getDrawable(requireContext(), R.drawable.pipe_divider));
|
||||
|
||||
pipe.setBounds(0, 0, (int) DimensionUnit.DP.toPixels(1f), (int) DimensionUnit.DP.toPixels(20f));
|
||||
suffixTextView.setCompoundDrawablesRelative(pipe, null, null, null);
|
||||
|
||||
LinearLayout suffixParent = (LinearLayout) suffixTextView.getParent();
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
|
||||
ViewUtil.setLeftMargin(suffixTextView, (int) DimensionUnit.DP.toPixels(16f));
|
||||
|
||||
binding.usernameTextWrapper.getSuffixTextView().setCompoundDrawablePadding((int) DimensionUnit.DP.toPixels(16f));
|
||||
|
||||
layoutParams.topMargin = suffixTextView.getPaddingTop();
|
||||
layoutParams.bottomMargin = suffixTextView.getPaddingBottom();
|
||||
|
||||
suffixProgress = new ImageView(requireContext());
|
||||
suffixProgress.setImageDrawable(UsernameSuffix.getInProgressDrawable(requireContext()));
|
||||
suffixParent.addView(suffixProgress, 0, layoutParams);
|
||||
|
||||
suffixTextView.setOnClickListener(this::onLearnMore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
suffixProgress = null;
|
||||
}
|
||||
|
||||
private void onLearnMore(@Nullable View unused) {
|
||||
new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(new StringBuilder("#\n").append(getString(R.string.UsernameEditFragment__what_is_this_number)))
|
||||
.setMessage(R.string.UsernameEditFragment__these_digits_help_keep)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void onUiStateChanged(@NonNull UsernameEditViewModel.State state) {
|
||||
EditText usernameInput = binding.usernameText;
|
||||
CircularProgressMaterialButton submitButton = binding.usernameSubmitButton;
|
||||
CircularProgressMaterialButton deleteButton = binding.usernameDeleteButton;
|
||||
TextInputLayout usernameInputWrapper = binding.usernameTextWrapper;
|
||||
|
||||
usernameInput.setEnabled(true);
|
||||
presentSuffix(state.getUsernameSuffix());
|
||||
|
||||
switch (state.getButtonState()) {
|
||||
case SUBMIT:
|
||||
|
@ -128,39 +197,57 @@ public class UsernameEditFragment extends LoggingFragment {
|
|||
|
||||
switch (state.getUsernameStatus()) {
|
||||
case NONE:
|
||||
usernameSubtext.setText("");
|
||||
usernameInputWrapper.setError(null);
|
||||
break;
|
||||
case TOO_SHORT:
|
||||
case TOO_LONG:
|
||||
usernameSubtext.setText(getResources().getString(R.string.UsernameEditFragment_usernames_must_be_between_a_and_b_characters, UsernameUtil.MIN_LENGTH, UsernameUtil.MAX_LENGTH));
|
||||
usernameSubtext.setTextColor(getResources().getColor(R.color.core_red));
|
||||
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:
|
||||
usernameSubtext.setText(R.string.UsernameEditFragment_usernames_can_only_include);
|
||||
usernameSubtext.setTextColor(getResources().getColor(R.color.core_red));
|
||||
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:
|
||||
usernameSubtext.setText(R.string.UsernameEditFragment_usernames_cannot_begin_with_a_number);
|
||||
usernameSubtext.setTextColor(getResources().getColor(R.color.core_red));
|
||||
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:
|
||||
usernameSubtext.setText(R.string.UsernameEditFragment_username_is_invalid);
|
||||
usernameSubtext.setTextColor(getResources().getColor(R.color.core_red));
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_username_is_invalid));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
case TAKEN:
|
||||
usernameSubtext.setText(R.string.UsernameEditFragment_this_username_is_taken);
|
||||
usernameSubtext.setTextColor(getResources().getColor(R.color.core_red));
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_this_username_is_taken));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
|
||||
|
||||
break;
|
||||
case AVAILABLE:
|
||||
usernameSubtext.setText(R.string.UsernameEditFragment_this_username_is_available);
|
||||
usernameSubtext.setTextColor(getResources().getColor(R.color.core_green));
|
||||
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_this_username_is_available));
|
||||
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_accent_green)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void presentSuffix(@NonNull UsernameSuffix usernameSuffix) {
|
||||
binding.usernameTextWrapper.setSuffixText(usernameSuffix.getCharSequence());
|
||||
|
||||
boolean isInProgress = usernameSuffix.isInProgress();
|
||||
|
||||
if (isInProgress) {
|
||||
suffixProgress.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
suffixProgress.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void onEvent(@NonNull UsernameEditViewModel.Event event) {
|
||||
switch (event) {
|
||||
case SUBMIT_SUCCESS:
|
||||
ResultContract.setUsernameCreated(getParentFragmentManager());
|
||||
Toast.makeText(requireContext(), R.string.UsernameEditFragment_successfully_set_username, Toast.LENGTH_SHORT).show();
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
break;
|
||||
|
@ -179,4 +266,23 @@ public class UsernameEditFragment extends LoggingFragment {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static class ResultContract extends FragmentResultContract<Boolean> {
|
||||
private static final String REQUEST_KEY = "username_created";
|
||||
|
||||
protected ResultContract() {
|
||||
super(REQUEST_KEY);
|
||||
}
|
||||
|
||||
static void setUsernameCreated(@NonNull FragmentManager fragmentManager) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(REQUEST_KEY, true);
|
||||
fragmentManager.setFragmentResult(REQUEST_KEY, bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean getResult(@NonNull Bundle bundle) {
|
||||
return bundle.getBoolean(REQUEST_KEY, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.text.TextUtils;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
|
@ -16,81 +15,80 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
|||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
import org.thoughtcrime.securesms.util.UsernameUtil;
|
||||
import org.thoughtcrime.securesms.util.UsernameUtil.InvalidReason;
|
||||
import org.thoughtcrime.securesms.util.rx.RxStore;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
|
||||
class UsernameEditViewModel extends ViewModel {
|
||||
|
||||
private static final String TAG = Log.tag(UsernameEditViewModel.class);
|
||||
|
||||
private final Application application;
|
||||
private final MutableLiveData<State> uiState;
|
||||
private final SingleLiveEvent<Event> events;
|
||||
private final UsernameEditRepository repo;
|
||||
private final RxStore<State> uiState;
|
||||
|
||||
private UsernameEditViewModel() {
|
||||
this.application = ApplicationDependencies.getApplication();
|
||||
this.repo = new UsernameEditRepository();
|
||||
this.uiState = new MutableLiveData<>();
|
||||
this.uiState = new RxStore<>(new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, UsernameSuffix.NONE), Schedulers.computation());
|
||||
this.events = new SingleLiveEvent<>();
|
||||
|
||||
uiState.setValue(new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE));
|
||||
}
|
||||
|
||||
void onUsernameUpdated(@NonNull String username) {
|
||||
if (TextUtils.isEmpty(username) && Recipient.self().getUsername().isPresent()) {
|
||||
uiState.setValue(new State(ButtonState.DELETE, UsernameStatus.NONE));
|
||||
return;
|
||||
}
|
||||
uiState.update(state -> {
|
||||
if (TextUtils.isEmpty(username) && Recipient.self().getUsername().isPresent()) {
|
||||
return new State(ButtonState.DELETE, UsernameStatus.NONE, state.usernameSuffix);
|
||||
}
|
||||
|
||||
if (username.equals(Recipient.self().getUsername().orElse(null))) {
|
||||
uiState.setValue(new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE));
|
||||
return;
|
||||
}
|
||||
if (username.equals(Recipient.self().getUsername().orElse(null))) {
|
||||
return new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, state.usernameSuffix);
|
||||
}
|
||||
|
||||
Optional<InvalidReason> invalidReason = UsernameUtil.checkUsername(username);
|
||||
Optional<InvalidReason> invalidReason = UsernameUtil.checkUsername(username);
|
||||
|
||||
if (invalidReason.isPresent()) {
|
||||
uiState.setValue(new State(ButtonState.SUBMIT_DISABLED, mapUsernameError(invalidReason.get())));
|
||||
return;
|
||||
}
|
||||
|
||||
uiState.setValue(new State(ButtonState.SUBMIT, UsernameStatus.NONE));
|
||||
return invalidReason.map(reason -> new State(ButtonState.SUBMIT_DISABLED, mapUsernameError(reason), state.usernameSuffix))
|
||||
.orElseGet(() -> new State(ButtonState.SUBMIT, UsernameStatus.NONE, state.usernameSuffix));
|
||||
});
|
||||
}
|
||||
|
||||
void onUsernameSubmitted(@NonNull String username) {
|
||||
if (username.equals(Recipient.self().getUsername().orElse(null))) {
|
||||
uiState.setValue(new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE));
|
||||
uiState.update(state -> new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, state.usernameSuffix));
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<InvalidReason> invalidReason = UsernameUtil.checkUsername(username);
|
||||
|
||||
if (invalidReason.isPresent()) {
|
||||
uiState.setValue(new State(ButtonState.SUBMIT_DISABLED, mapUsernameError(invalidReason.get())));
|
||||
uiState.update(state -> new State(ButtonState.SUBMIT_DISABLED, mapUsernameError(invalidReason.get()), state.usernameSuffix));
|
||||
return;
|
||||
}
|
||||
|
||||
uiState.setValue(new State(ButtonState.SUBMIT_LOADING, UsernameStatus.NONE));
|
||||
uiState.update(state -> new State(ButtonState.SUBMIT_LOADING, UsernameStatus.NONE, state.usernameSuffix));
|
||||
|
||||
repo.setUsername(username, (result) -> {
|
||||
ThreadUtil.runOnMain(() -> {
|
||||
switch (result) {
|
||||
case SUCCESS:
|
||||
uiState.setValue(new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE));
|
||||
uiState.update(state -> new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, state.usernameSuffix));
|
||||
events.postValue(Event.SUBMIT_SUCCESS);
|
||||
break;
|
||||
case USERNAME_INVALID:
|
||||
uiState.setValue(new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.INVALID_GENERIC));
|
||||
uiState.update(state -> new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.INVALID_GENERIC, state.usernameSuffix));
|
||||
events.postValue(Event.SUBMIT_FAIL_INVALID);
|
||||
break;
|
||||
case USERNAME_UNAVAILABLE:
|
||||
uiState.setValue(new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.TAKEN));
|
||||
uiState.update(state -> new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.TAKEN, state.usernameSuffix));
|
||||
events.postValue(Event.SUBMIT_FAIL_TAKEN);
|
||||
break;
|
||||
case NETWORK_ERROR:
|
||||
uiState.setValue(new State(ButtonState.SUBMIT, UsernameStatus.NONE));
|
||||
uiState.update(state -> new State(ButtonState.SUBMIT, UsernameStatus.NONE, state.usernameSuffix));
|
||||
events.postValue(Event.NETWORK_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
@ -99,17 +97,17 @@ class UsernameEditViewModel extends ViewModel {
|
|||
}
|
||||
|
||||
void onUsernameDeleted() {
|
||||
uiState.setValue(new State(ButtonState.DELETE_LOADING, UsernameStatus.NONE));
|
||||
uiState.update(state -> new State(ButtonState.DELETE_LOADING, UsernameStatus.NONE, state.usernameSuffix));
|
||||
|
||||
repo.deleteUsername((result) -> {
|
||||
ThreadUtil.runOnMain(() -> {
|
||||
switch (result) {
|
||||
case SUCCESS:
|
||||
uiState.postValue(new State(ButtonState.DELETE_DISABLED, UsernameStatus.NONE));
|
||||
uiState.update(state -> new State(ButtonState.DELETE_DISABLED, UsernameStatus.NONE, state.usernameSuffix));
|
||||
events.postValue(Event.DELETE_SUCCESS);
|
||||
break;
|
||||
case NETWORK_ERROR:
|
||||
uiState.postValue(new State(ButtonState.DELETE, UsernameStatus.NONE));
|
||||
uiState.update(state -> new State(ButtonState.DELETE, UsernameStatus.NONE, state.usernameSuffix));
|
||||
events.postValue(Event.NETWORK_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
@ -117,8 +115,8 @@ class UsernameEditViewModel extends ViewModel {
|
|||
});
|
||||
}
|
||||
|
||||
@NonNull LiveData<State> getUiState() {
|
||||
return uiState;
|
||||
@NonNull Flowable<State> getUiState() {
|
||||
return uiState.getStateFlowable().observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
@NonNull LiveData<Event> getEvents() {
|
||||
|
@ -138,12 +136,15 @@ class UsernameEditViewModel extends ViewModel {
|
|||
static class State {
|
||||
private final ButtonState buttonState;
|
||||
private final UsernameStatus usernameStatus;
|
||||
private final UsernameSuffix usernameSuffix;
|
||||
|
||||
private State(@NonNull ButtonState buttonState,
|
||||
@NonNull UsernameStatus usernameStatus)
|
||||
@NonNull UsernameStatus usernameStatus,
|
||||
@NonNull UsernameSuffix usernameSuffix)
|
||||
{
|
||||
this.buttonState = buttonState;
|
||||
this.usernameStatus = usernameStatus;
|
||||
this.usernameSuffix = usernameSuffix;
|
||||
}
|
||||
|
||||
@NonNull ButtonState getButtonState() {
|
||||
|
@ -153,6 +154,10 @@ class UsernameEditViewModel extends ViewModel {
|
|||
@NonNull UsernameStatus getUsernameStatus() {
|
||||
return usernameStatus;
|
||||
}
|
||||
|
||||
@NonNull UsernameSuffix getUsernameSuffix() {
|
||||
return usernameSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
enum UsernameStatus {
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
package org.thoughtcrime.securesms.profiles.manage
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.signal.core.util.DimensionUnit
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsBottomSheetFragment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.FragmentResultContract
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
|
||||
/**
|
||||
* Allows the user to either share their username directly or to copy it to their clipboard.
|
||||
*/
|
||||
class UsernameShareBottomSheet : DSLSettingsBottomSheetFragment() {
|
||||
|
||||
companion object {
|
||||
private const val REQUEST_KEY = "copy_username"
|
||||
}
|
||||
|
||||
private val lifecycleDisposable = LifecycleDisposable()
|
||||
|
||||
override fun bindAdapter(adapter: DSLSettingsAdapter) {
|
||||
CopyButton.register(adapter)
|
||||
ShareButton.register(adapter)
|
||||
|
||||
lifecycleDisposable += Recipient.observable(Recipient.self().id).subscribe {
|
||||
adapter.submitList(getConfiguration(it).toMappingModelList())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConfiguration(recipient: Recipient): DSLConfiguration {
|
||||
return configure {
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
R.string.UsernameShareBottomSheet__copy_or_share_a_username_link,
|
||||
DSLSettingsText.TextAppearanceModifier(R.style.Signal_Text_BodyMedium),
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.ColorModifier(
|
||||
ContextCompat.getColor(requireContext(), R.color.signal_colorOnSurfaceVariant),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
space(DimensionUnit.DP.toPixels(32f).toInt())
|
||||
|
||||
val username = recipient.username.get()
|
||||
customPref(
|
||||
CopyButton.Model(
|
||||
text = username,
|
||||
onClick = {
|
||||
copyToClipboard(it)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
space(DimensionUnit.DP.toPixels(20f).toInt())
|
||||
|
||||
customPref(
|
||||
CopyButton.Model(
|
||||
text = getString(R.string.signal_me_url, username),
|
||||
onClick = {
|
||||
copyToClipboard(it)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
space(DimensionUnit.DP.toPixels(24f).toInt())
|
||||
|
||||
customPref(
|
||||
ShareButton.Model(
|
||||
text = getString(R.string.signal_me_url, username),
|
||||
onClick = {
|
||||
openShareSheet(it.text)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
space(DimensionUnit.DP.toPixels(18f).toInt())
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyToClipboard(model: CopyButton.Model) {
|
||||
Util.copyToClipboard(requireContext(), model.text)
|
||||
setFragmentResult(REQUEST_KEY, Bundle().apply { putBoolean(REQUEST_KEY, true) })
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
|
||||
private fun openShareSheet(charSequence: CharSequence) {
|
||||
val mimeType = Intent.normalizeMimeType("text/plain")
|
||||
val shareIntent = ShareCompat.IntentBuilder(requireContext())
|
||||
.setText(charSequence)
|
||||
.setType(mimeType)
|
||||
.createChooserIntent()
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
||||
try {
|
||||
startActivity(shareIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Toast.makeText(requireContext(), R.string.MediaPreviewActivity_cant_find_an_app_able_to_share_this_media, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
object ResultContract : FragmentResultContract<Boolean>(REQUEST_KEY) {
|
||||
override fun getResult(bundle: Bundle): Boolean {
|
||||
return bundle.getBoolean(REQUEST_KEY, false)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package org.thoughtcrime.securesms.profiles.manage
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.android.material.progressindicator.CircularProgressIndicatorSpec
|
||||
import com.google.android.material.progressindicator.IndeterminateDrawable
|
||||
import org.signal.core.util.DimensionUnit
|
||||
import org.thoughtcrime.securesms.R
|
||||
|
||||
/**
|
||||
* Describes the state of the username suffix, which is a spanned CharSequence.
|
||||
*/
|
||||
data class UsernameSuffix(
|
||||
val charSequence: CharSequence?
|
||||
) {
|
||||
|
||||
val isInProgress = charSequence == null
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val LOADING = UsernameSuffix(null)
|
||||
|
||||
@JvmField
|
||||
val NONE = UsernameSuffix("")
|
||||
|
||||
@JvmStatic
|
||||
fun fromCode(code: Int) = UsernameSuffix("#$code")
|
||||
|
||||
@JvmStatic
|
||||
fun getInProgressDrawable(context: Context): IndeterminateDrawable<CircularProgressIndicatorSpec> {
|
||||
val progressIndicatorSpec = CircularProgressIndicatorSpec(context, null).apply {
|
||||
indicatorInset = 0
|
||||
indicatorSize = DimensionUnit.DP.toPixels(16f).toInt()
|
||||
trackColor = ContextCompat.getColor(context, R.color.signal_colorOnSurfaceVariant)
|
||||
trackThickness = DimensionUnit.DP.toPixels(1f).toInt()
|
||||
}
|
||||
|
||||
return IndeterminateDrawable.createCircularDrawable(context, progressIndicatorSpec).apply {
|
||||
setBounds(0, 0, DimensionUnit.DP.toPixels(16f).toInt(), DimensionUnit.DP.toPixels(16f).toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.thoughtcrime.securesms.util
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import java.util.function.Consumer
|
||||
|
||||
/**
|
||||
* Generic Fragment result contract.
|
||||
*/
|
||||
abstract class FragmentResultContract<T> protected constructor(private val resultKey: String) {
|
||||
|
||||
protected abstract fun getResult(bundle: Bundle): T
|
||||
|
||||
fun registerForResult(fragmentManager: FragmentManager, lifecycleOwner: LifecycleOwner, consumer: Consumer<T>) {
|
||||
fragmentManager.setFragmentResultListener(resultKey, lifecycleOwner) { key, bundle ->
|
||||
if (key == resultKey) {
|
||||
val result = getResult(bundle)
|
||||
consumer.accept(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package org.thoughtcrime.securesms.util.adapter.mapping
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.viewbinding.ViewBinding
|
||||
|
||||
/**
|
||||
* Allows ViewHolders to be generated with a ViewBinding. Intended usage is as follows:
|
||||
*
|
||||
* BindingFactory(::MyBindingViewHolder, MyBinding::inflate)
|
||||
*/
|
||||
class BindingFactory<T : MappingModel<T>, B : ViewBinding>(
|
||||
private val creator: (B) -> BindingViewHolder<T, B>,
|
||||
private val inflater: (LayoutInflater, ViewGroup, Boolean) -> B
|
||||
) : Factory<T> {
|
||||
override fun createViewHolder(parent: ViewGroup): MappingViewHolder<T> {
|
||||
val binding = inflater(LayoutInflater.from(parent.context), parent, false)
|
||||
return creator(binding)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package org.thoughtcrime.securesms.util.adapter.mapping
|
||||
|
||||
import androidx.viewbinding.ViewBinding
|
||||
|
||||
/**
|
||||
* A ViewHolder which is populated with a ViewBinding, used in conjunction with BindingFactory
|
||||
*/
|
||||
abstract class BindingViewHolder<T, B : ViewBinding>(protected val binding: B) : MappingViewHolder<T>(binding.root)
|
|
@ -13,7 +13,7 @@ import io.reactivex.rxjava3.subjects.PublishSubject
|
|||
*/
|
||||
class RxStore<T : Any>(
|
||||
defaultValue: T,
|
||||
private val scheduler: Scheduler = Schedulers.computation()
|
||||
scheduler: Scheduler = Schedulers.computation()
|
||||
) {
|
||||
|
||||
private val behaviorProcessor = BehaviorProcessor.createDefault(defaultValue)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/signal_colorOutline" />
|
||||
</shape>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.button.MaterialButton 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"
|
||||
style="@style/Signal.Widget.Button.Base.Secondary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:minHeight="56dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/signal_colorOnSurface"
|
||||
app:cornerRadius="12dp"
|
||||
app:icon="@drawable/ic_copy_24"
|
||||
app:iconGravity="end"
|
||||
app:iconSize="24dp"
|
||||
app:iconTint="@color/signal_colorOnSurface"
|
||||
app:rippleColor="@color/signal_colorOutline"
|
||||
app:strokeColor="@color/signal_colorOutline_38"
|
||||
app:strokeWidth="1dp"
|
||||
tools:text="maya#2342" />
|
|
@ -1,297 +1,310 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
tools:viewBindingIgnore="true"
|
||||
<ScrollView 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:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/signal_m3_toolbar_height"
|
||||
android:minHeight="@dimen/signal_m3_toolbar_height"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navigationIcon="@drawable/ic_arrow_left_24"
|
||||
app:title="@string/CreateProfileActivity__profile" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/manage_profile_avatar_background"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/circle_tintable"
|
||||
android:tint="@color/core_grey_05"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||
app:layout_goneMarginTop="?attr/actionBarSize" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/manage_profile_avatar_placeholder"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:tint="@color/core_grey_75"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintEnd_toEndOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintStart_toStartOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintTop_toTopOf="@+id/manage_profile_avatar_background"
|
||||
app:srcCompat="@drawable/ic_profile_outline_40" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/manage_profile_avatar_initials"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/manage_profile_avatar_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/manage_profile_avatar_background"
|
||||
app:layout_constraintStart_toStartOf="@id/manage_profile_avatar_background"
|
||||
app:layout_constraintTop_toTopOf="@id/manage_profile_avatar_background"
|
||||
tools:ignore="SpUsage"
|
||||
tools:text="AF"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/manage_profile_avatar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:contentDescription="@string/CreateProfileActivity_set_avatar_description"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintEnd_toEndOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintStart_toStartOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintTop_toTopOf="@+id/manage_profile_avatar_background" />
|
||||
|
||||
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||
android:id="@+id/manage_profile_badge"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginStart="44dp"
|
||||
android:layout_marginTop="52dp"
|
||||
app:badge_size="large"
|
||||
app:layout_constraintStart_toStartOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintTop_toTopOf="@+id/manage_profile_avatar_background" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/manage_profile_edit_photo"
|
||||
style="@style/Widget.Signal.Button.Small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="14dp"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:text="@string/ManageProfileFragment__edit_photo"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_avatar_background" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/manage_profile_name_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:paddingStart="@dimen/dsl_settings_gutter"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="@dimen/dsl_settings_gutter"
|
||||
android:paddingBottom="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_edit_photo">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/manage_profile_name_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_profile_name_24"
|
||||
app:tint="@color/signal_text_primary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/manage_profile_name"
|
||||
app:layout_constraintBottom_toBottomOf="@id/manage_profile_name_subtitle"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/manage_profile_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/manage_profile_name_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:text="Peter Parker"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manage_profile_name_subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:text="@string/ManageProfileFragment_your_name"
|
||||
android:textColor="@color/signal_text_secondary"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_name"
|
||||
app:layout_constraintStart_toStartOf="@id/manage_profile_name"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/manage_profile_username_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/dsl_settings_gutter"
|
||||
android:paddingEnd="@dimen/dsl_settings_gutter"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:background="?selectableItemBackground"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_name_container">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/manage_profile_username_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_at_24"
|
||||
app:tint="@color/signal_text_primary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/manage_profile_username"
|
||||
app:layout_constraintBottom_toBottomOf="@id/manage_profile_username_subtitle"/>
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/signal_m3_toolbar_height"
|
||||
android:minHeight="@dimen/signal_m3_toolbar_height"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navigationIcon="@drawable/ic_arrow_left_24"
|
||||
app:title="@string/CreateProfileActivity__profile" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/manage_profile_username"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/manage_profile_username_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:text="\@spiderman"/>
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/manage_profile_avatar_background"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/circle_tintable"
|
||||
android:tint="@color/core_grey_05"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||
app:layout_goneMarginTop="?attr/actionBarSize" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manage_profile_username_subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:text="@string/ManageProfileFragment_your_username"
|
||||
android:textColor="@color/signal_text_secondary"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_username"
|
||||
app:layout_constraintStart_toStartOf="@id/manage_profile_username"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/manage_profile_avatar_placeholder"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:tint="@color/core_grey_75"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintEnd_toEndOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintStart_toStartOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintTop_toTopOf="@+id/manage_profile_avatar_background"
|
||||
app:srcCompat="@drawable/ic_profile_outline_40" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/manage_profile_avatar_initials"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/manage_profile_avatar_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/manage_profile_avatar_background"
|
||||
app:layout_constraintStart_toStartOf="@id/manage_profile_avatar_background"
|
||||
app:layout_constraintTop_toTopOf="@id/manage_profile_avatar_background"
|
||||
tools:ignore="SpUsage"
|
||||
tools:text="AF"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/manage_profile_avatar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:contentDescription="@string/CreateProfileActivity_set_avatar_description"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintEnd_toEndOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintStart_toStartOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintTop_toTopOf="@+id/manage_profile_avatar_background" />
|
||||
|
||||
<org.thoughtcrime.securesms.badges.BadgeImageView
|
||||
android:id="@+id/manage_profile_badge"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginStart="44dp"
|
||||
android:layout_marginTop="52dp"
|
||||
app:badge_size="large"
|
||||
app:layout_constraintStart_toStartOf="@+id/manage_profile_avatar_background"
|
||||
app:layout_constraintTop_toTopOf="@+id/manage_profile_avatar_background" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/manage_profile_edit_photo"
|
||||
style="@style/Widget.Signal.Button.Small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="14dp"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:text="@string/ManageProfileFragment__edit_photo"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_avatar_background" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/manage_profile_name_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:paddingStart="@dimen/dsl_settings_gutter"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="@dimen/dsl_settings_gutter"
|
||||
android:paddingBottom="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_edit_photo">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/manage_profile_name_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@id/manage_profile_name_subtitle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/manage_profile_name"
|
||||
app:srcCompat="@drawable/ic_profile_name_24"
|
||||
app:tint="@color/signal_text_primary" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/manage_profile_name"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/manage_profile_name_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Peter Parker" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manage_profile_name_subtitle"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/ManageProfileFragment_your_name"
|
||||
android:textColor="@color/signal_text_secondary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/manage_profile_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_name" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/manage_profile_username_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:minHeight="72dp"
|
||||
android:paddingStart="@dimen/dsl_settings_gutter"
|
||||
android:paddingEnd="@dimen/safety_number_recipient_row_item_gutter"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_name_container">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/manage_profile_username_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@id/manage_profile_username_subtitle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/manage_profile_username"
|
||||
app:srcCompat="@drawable/ic_at_24"
|
||||
app:tint="@color/signal_text_primary" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/manage_profile_username"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintBottom_toTopOf="@id/manage_profile_username_subtitle"
|
||||
app:layout_constraintEnd_toStartOf="@id/manage_profile_username_share"
|
||||
app:layout_constraintStart_toEndOf="@id/manage_profile_username_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_goneMarginEnd="48dp"
|
||||
tools:text="\@spiderman" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manage_profile_username_subtitle"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/ManageProfileFragment_your_username"
|
||||
android:textColor="@color/signal_text_secondary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/manage_profile_username_share"
|
||||
app:layout_constraintStart_toStartOf="@id/manage_profile_username"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_username"
|
||||
app:layout_goneMarginEnd="48dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/manage_profile_username_share"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_share_24"
|
||||
app:tint="@color/signal_colorOnSurface" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/manage_profile_about_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:paddingStart="@dimen/dsl_settings_gutter"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="@dimen/dsl_settings_gutter"
|
||||
android:paddingBottom="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_username_container">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/manage_profile_about_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toBottomOf="@id/manage_profile_about_subtitle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/manage_profile_about"
|
||||
app:srcCompat="@drawable/ic_compose_24"
|
||||
app:tint="@color/signal_text_primary" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/manage_profile_about"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:textAlignment="viewStart"
|
||||
app:emoji_forceCustom="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/manage_profile_about_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Photographer for the Daily Bugle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manage_profile_about_subtitle"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/ManageProfileFragment_write_a_few_words_about_yourself"
|
||||
android:textColor="@color/signal_text_secondary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/manage_profile_about"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_about" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/manage_profile_badges_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:paddingStart="@dimen/dsl_settings_gutter"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="@dimen/dsl_settings_gutter"
|
||||
android:paddingBottom="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_about_container">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/manage_profile_badges_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toBottomOf="@id/manage_profile_badges"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/manage_profile_badges"
|
||||
app:srcCompat="@drawable/ic_badge_24"
|
||||
app:tint="@color/signal_text_primary" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/manage_profile_badges"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:text="@string/ManageProfileFragment_badges"
|
||||
android:textAlignment="viewStart"
|
||||
app:emoji_forceCustom="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/manage_profile_badges_icon"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
|
||||
android:id="@+id/group_description_text"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/CreateProfileActivity_signal_profiles_are_end_to_end_encrypted"
|
||||
android:textAppearance="@style/Signal.Text.BodyMedium"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/manage_profile_badges_container"
|
||||
app:layout_constraintVertical_bias="1.0" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/manage_profile_about_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/dsl_settings_gutter"
|
||||
android:paddingEnd="@dimen/dsl_settings_gutter"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:background="?selectableItemBackground"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_username_container">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/manage_profile_about_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_compose_24"
|
||||
app:tint="@color/signal_text_primary"
|
||||
app:layout_constraintTop_toTopOf="@id/manage_profile_about"
|
||||
app:layout_constraintBottom_toBottomOf="@id/manage_profile_about_subtitle"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/manage_profile_about"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/manage_profile_about_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:emoji_forceCustom="true"
|
||||
tools:text="Photographer for the Daily Bugle"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manage_profile_about_subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:text="@string/ManageProfileFragment_write_a_few_words_about_yourself"
|
||||
android:textColor="@color/signal_text_secondary"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_about"
|
||||
app:layout_constraintStart_toStartOf="@id/manage_profile_about"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/manage_profile_badges_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/dsl_settings_gutter"
|
||||
android:paddingEnd="@dimen/dsl_settings_gutter"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:background="?selectableItemBackground"
|
||||
app:layout_constraintTop_toBottomOf="@id/manage_profile_about_container">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/manage_profile_badges_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_badge_24"
|
||||
app:tint="@color/signal_text_primary"
|
||||
app:layout_constraintTop_toTopOf="@id/manage_profile_badges"
|
||||
app:layout_constraintBottom_toBottomOf="@id/manage_profile_badges"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/manage_profile_badges"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/manage_profile_badges_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:text="@string/ManageProfileFragment_badges"
|
||||
app:emoji_forceCustom="true" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
|
||||
android:id="@+id/description_text"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:text="@string/CreateProfileActivity_signal_profiles_are_end_to_end_encrypted"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
android:textAppearance="@style/Signal.Text.BodyMedium"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/manage_profile_badges_container"
|
||||
app:layout_constraintVertical_bias="1.0" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:viewBindingIgnore="true"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -37,13 +36,12 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/TextAppearance.Signal.Title2.Bold"
|
||||
android:layout_width="wrap_content"
|
||||
style="@style/Signal.Text.TitleLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:paddingHorizontal="@dimen/dsl_settings_gutter"
|
||||
android:text="@string/CreateProfileActivity_set_up_your_profile"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
|
@ -52,15 +50,28 @@
|
|||
app:layout_constraintVertical_bias="0.0"
|
||||
app:layout_constraintVertical_chainStyle="spread_inside" />
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
|
||||
android:id="@+id/profile_description_text"
|
||||
style="@style/Signal.Text.BodyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="11dp"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/ProfileCreateFragment__profiles_are_only_visible_to_people_you_message"
|
||||
android:textColor="@color/core_grey_60"
|
||||
app:layout_constraintTop_toBottomOf="@id/title" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/avatar_background"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="96dp"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/circle_tintable"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
||||
app:layout_constraintTop_toBottomOf="@+id/profile_description_text"
|
||||
app:layout_goneMarginTop="?attr/actionBarSize" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
|
@ -100,8 +111,8 @@
|
|||
android:id="@+id/camera_icon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="56dp"
|
||||
android:layout_marginTop="56dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="@drawable/circle_tintable_padded"
|
||||
android:cropToPadding="false"
|
||||
android:elevation="4dp"
|
||||
|
@ -122,66 +133,131 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/name_preview">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
|
||||
android:id="@+id/given_name"
|
||||
style="@style/Signal.Text.Body"
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/given_name_wrapper"
|
||||
style="@style/Widget.Signal.TextInputLayout.FilledBox.ContactNameEditor"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="13dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_weight="1"
|
||||
android:autofillHints="personGivenName"
|
||||
android:hint="@string/CreateProfileActivity_first_name_required"
|
||||
android:inputType="textPersonName"
|
||||
android:singleLine="true" />
|
||||
android:hint="@string/CreateProfileActivity_first_name_required">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
|
||||
android:id="@+id/given_name"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:autofillHints="personGivenName"
|
||||
android:inputType="textPersonName"
|
||||
android:singleLine="true" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/mms_group_hint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:text="@string/CreateProfileActivity_custom_mms_group_names_and_photos_will_only_be_visible_to_you"
|
||||
android:textAppearance="@style/Signal.Text.Caption"
|
||||
android:textColor="@color/signal_text_secondary"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
|
||||
android:id="@+id/family_name"
|
||||
style="@style/Signal.Text.Body"
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/family_name_wrapper"
|
||||
style="@style/Widget.Signal.TextInputLayout.FilledBox.ContactNameEditor"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="13dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_weight="1"
|
||||
android:autofillHints="personFamilyName"
|
||||
android:hint="@string/CreateProfileActivity_last_name_optional"
|
||||
android:inputType="textPersonName"
|
||||
android:singleLine="true" />
|
||||
android:hint="@string/CreateProfileActivity_last_name_optional">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
|
||||
android:id="@+id/family_name"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:autofillHints="personFamilyName"
|
||||
android:inputType="textPersonName"
|
||||
android:singleLine="true" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
|
||||
android:id="@+id/description_text"
|
||||
style="@style/Signal.Text.Preview"
|
||||
android:id="@+id/group_description_text"
|
||||
style="@style/Signal.Text.BodyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="11dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/CreateProfileActivity_signal_profiles_are_end_to_end_encrypted"
|
||||
android:text="@string/CreateProfileActivity_group_descriptions_will_be_visible_to_members_of_this_group_and_people_who_have_been_invited"
|
||||
android:textColor="@color/core_grey_60"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/name_container"
|
||||
app:layout_constraintVertical_bias="1.0" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/who_can_find_me_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="23dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:minHeight="72dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/group_description_text"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/who_can_find_me_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_group_24" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/who_can_find_me_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:text="@string/ProfileCreateFragment__who_can_find_me"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/signal_colorOnSurface"
|
||||
app:layout_constraintBottom_toTopOf="@id/who_can_find_me_description"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/who_can_find_me_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/who_can_find_me_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:textAppearance="@style/Signal.Text.BodyMedium"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/who_can_find_me_icon"
|
||||
app:layout_constraintTop_toBottomOf="@id/who_can_find_me_title"
|
||||
tools:text="@string/PhoneNumberPrivacy_everyone" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
@ -196,8 +272,8 @@
|
|||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:enabled="false"
|
||||
app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Primary"
|
||||
app:circularProgressMaterialButton__label="@string/CreateProfileActivity_next" />
|
||||
app:circularProgressMaterialButton__label="@string/CreateProfileActivity_next"
|
||||
app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Primary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/share_button"
|
||||
style="@style/Signal.Widget.Button.Medium.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="@string/preferences_share"
|
||||
app:icon="@drawable/ic_share_24"
|
||||
app:iconSize="20dp" />
|
||||
</FrameLayout>
|
|
@ -1,7 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:viewBindingIgnore="true"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -10,59 +9,94 @@
|
|||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_height="@dimen/signal_m3_toolbar_height"
|
||||
android:minHeight="@dimen/signal_m3_toolbar_height"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navigationIcon="@drawable/ic_arrow_left_24"
|
||||
app:title="@string/UsernameEditFragment_username" />
|
||||
app:navigationIcon="@drawable/ic_x_24"
|
||||
app:title="@string/UsernameEditFragment_username"
|
||||
app:titleTextAppearance="@style/Signal.Text.TitleLarge" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/username_text"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:hint="@string/UsernameEditFragment_username"
|
||||
android:imeOptions="actionDone"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"
|
||||
android:minHeight="56dp"
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="@drawable/circle_tintable"
|
||||
android:importantForAccessibility="no"
|
||||
android:padding="14dp"
|
||||
app:backgroundTint="@color/signal_colorSurface2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar" />
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||
app:srcCompat="@drawable/ic_at_24" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username_subtext"
|
||||
style="@style/Signal.Text.Caption"
|
||||
android:layout_width="0dp"
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/username_text_wrapper"
|
||||
style="@style/Widget.Signal.TextInputLayout.FilledBox.ContactNameEditor"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="24dp"
|
||||
app:boxStrokeColor="@color/signal_colorPrimary"
|
||||
app:boxStrokeWidthFocused="2dp"
|
||||
app:expandedHintEnabled="false"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/username_text"
|
||||
tools:text="Some error code" />
|
||||
app:layout_constraintTop_toBottomOf="@id/summary"
|
||||
app:suffixTextColor="@color/signal_colorOnSurface"
|
||||
tools:suffixText="| #1234">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/username_text"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/UsernameEditFragment_username"
|
||||
android:imeOptions="actionDone"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"
|
||||
android:minHeight="56dp">
|
||||
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:gravity="center"
|
||||
android:text="@string/UsernameEditFragment__choose_your_username"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/icon" />
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
|
||||
android:id="@+id/username_description"
|
||||
style="@style/Signal.Text.Caption"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/UsernameEditFragment_usernames_on_signal_are_optional"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="@string/UsernameEditFragment__usernames_let_others_message"
|
||||
android:textAppearance="@style/Signal.Text.BodyMedium"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintBottom_toTopOf="@id/username_button_barrier"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/username_subtext" />
|
||||
app:layout_constraintTop_toBottomOf="@id/username_text_wrapper"
|
||||
app:layout_constraintVertical_bias="0" />
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton
|
||||
android:id="@+id/username_submit_button"
|
||||
|
|
|
@ -37,6 +37,14 @@
|
|||
app:nullable="true" />
|
||||
</action>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_createProfileFragment_to_phoneNumberPrivacy"
|
||||
app:destination="@id/phoneNumberPrivacy"
|
||||
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" />
|
||||
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -47,4 +55,10 @@
|
|||
|
||||
<include app:graph="@navigation/avatar_picker" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/phoneNumberPrivacy"
|
||||
android:name="org.thoughtcrime.securesms.profiles.edit.pnp.WhoCanSeeMyPhoneNumberFragment"
|
||||
android:label="fragment_phone_number_privacy"
|
||||
tools:layout="@layout/dsl_settings_fragment" />
|
||||
|
||||
</navigation>
|
|
@ -62,6 +62,9 @@
|
|||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_manageProfileFragment_to_shareUsernameDialog"
|
||||
app:destination="@id/shareUsernameDialog" />
|
||||
|
||||
</fragment>
|
||||
|
||||
|
@ -87,4 +90,9 @@
|
|||
|
||||
<include app:graph="@navigation/avatar_picker" />
|
||||
|
||||
<dialog
|
||||
android:id="@+id/shareUsernameDialog"
|
||||
android:name="org.thoughtcrime.securesms.profiles.manage.UsernameShareBottomSheet"
|
||||
android:label="fragment_username_share" />
|
||||
|
||||
</navigation>
|
|
@ -11,6 +11,7 @@
|
|||
<string name="sustainer_boost_and_badges" translatable="false">https://support.signal.org/hc/articles/4408365318426</string>
|
||||
<string name="google_pay_url" translatable="false">https://pay.google.com</string>
|
||||
<string name="donation_decline_code_error_url" translatable="false">https://support.signal.org/hc/articles/4408365318426#errors</string>
|
||||
<string name="signal_me_url" translatable="false">https://signal.me/%1$s</string>
|
||||
|
||||
<string name="yes">Yes</string>
|
||||
<string name="no">No</string>
|
||||
|
@ -503,6 +504,20 @@
|
|||
<string name="CreateProfileActivity_signal_profiles_are_end_to_end_encrypted">Your profile is end-to-end encrypted. Your profile and changes to it will be visible to your contacts, when you initiate or accept new conversations, and when you join new groups.</string>
|
||||
<string name="CreateProfileActivity_set_avatar_description">Set avatar</string>
|
||||
|
||||
<!-- ProfileCreateFragment -->
|
||||
<!-- Displayed at the top of the screen and explains how profiles can be viewed. -->
|
||||
<string name="ProfileCreateFragment__profiles_are_only_visible_to_people_you_message">Profiles are only visible to people you message.</string>
|
||||
<!-- Title of clickable row to select phone number privacy settings -->
|
||||
<string name="ProfileCreateFragment__who_can_find_me">Who can find me by number?</string>
|
||||
|
||||
<!-- WhoCanSeeMyPhoneNumberFragment -->
|
||||
<!-- Toolbar title for this screen -->
|
||||
<string name="WhoCanSeeMyPhoneNumberFragment__who_can_find_me_by_number">Who can find me by number?</string>
|
||||
<!-- Description for radio item stating anyone can see your phone number -->
|
||||
<string name="WhoCanSeeMyPhoneNumberFragment__anyone_who_has">Anyone who has your phone number in their contacts will see you as a contact on Signal. Others will be able to find you with your number in search.</string>
|
||||
<!-- Description for radio item stating no one will be able to see your phone number -->
|
||||
<string name="WhoCanSeeMyPhoneNumberFragment__nobody_on_signal">Nobody on Signal will be able to find you with your phone number.</string>
|
||||
|
||||
<!-- ChooseBackupFragment -->
|
||||
<string name="ChooseBackupFragment__restore_from_backup">Restore from backup?</string>
|
||||
<string name="ChooseBackupFragment__restore_your_messages_and_media">Restore your messages and media from a local backup. If you don\'t restore now, you won\'t be able to restore later.</string>
|
||||
|
@ -856,6 +871,11 @@
|
|||
<string name="ManageProfileFragment_failed_to_set_avatar">Failed to set avatar</string>
|
||||
<string name="ManageProfileFragment_badges">Badges</string>
|
||||
<string name="ManageProfileFragment__edit_photo">Edit photo</string>
|
||||
<!-- Snackbar message after creating username -->
|
||||
<string name="ManageProfileFragment__username_created">Username created</string>
|
||||
<!-- Snackbar message after copying username -->
|
||||
<string name="ManageProfileFragment__username_copied">Username copied</string>
|
||||
|
||||
|
||||
<!-- ManageRecipientActivity -->
|
||||
<string name="ManageRecipientActivity_no_groups_in_common">No groups in common</string>
|
||||
|
@ -1851,6 +1871,8 @@
|
|||
<string name="UnverifiedSendDialog_send">Send</string>
|
||||
|
||||
<!-- UsernameEditFragment -->
|
||||
<!-- Instructional text at the top of the username edit screen -->
|
||||
<string name="UsernameEditFragment__choose_your_username">Choose your username</string>
|
||||
<string name="UsernameEditFragment_username">Username</string>
|
||||
<string name="UsernameEditFragment_delete">Delete</string>
|
||||
<string name="UsernameEditFragment_successfully_set_username">Successfully set username.</string>
|
||||
|
@ -1862,13 +1884,21 @@
|
|||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Usernames cannot begin with a number.</string>
|
||||
<string name="UsernameEditFragment_username_is_invalid">Username is invalid.</string>
|
||||
<string name="UsernameEditFragment_usernames_must_be_between_a_and_b_characters">Usernames must be between %1$d and %2$d characters.</string>
|
||||
<string name="UsernameEditFragment_usernames_on_signal_are_optional">Usernames on Signal are optional. If you choose to create a username, other Signal users will be able to find you by this username and contact you without knowing your phone number.</string>
|
||||
<!-- Explanation about what usernames provide -->
|
||||
<string name="UsernameEditFragment__usernames_let_others_message">Usernames let others message you without needing your phone number. They are paired with a set of digits to help keep your address private.</string>
|
||||
<!-- 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__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>
|
||||
|
||||
<plurals name="UserNotificationMigrationJob_d_contacts_are_on_signal">
|
||||
<item quantity="one">%d contact is on Signal!</item>
|
||||
<item quantity="other">%d contacts are on Signal!</item>
|
||||
</plurals>
|
||||
|
||||
<!-- UsernameShareBottomSheet -->
|
||||
<!-- Explanation of what the sheet enables the user to do -->
|
||||
<string name="UsernameShareBottomSheet__copy_or_share_a_username_link">Copy or share a username link</string>
|
||||
|
||||
<!-- VerifyIdentityActivity -->
|
||||
<string name="VerifyIdentityActivity_your_contact_is_running_an_old_version_of_signal">Your contact is running an old version of Signal. Please ask them to update before verifying your safety number.</string>
|
||||
<string name="VerifyIdentityActivity_your_contact_is_running_a_newer_version_of_Signal">Your contact is running a newer version of Signal with an incompatible QR code format. Please update to compare.</string>
|
||||
|
|
Ładowanie…
Reference in New Issue