Fix several issues with new avatar picker.

* Fix silliness with text behaviour
* Fix long click behaviour
* Make views play nicer with landscape mode
* Do not show megaphone if user has an avatar (or had one and removed it)
* Fix bad heading on vector color picker
This commit is contained in:
Alex Hart 2021-07-22 13:28:03 -03:00 committed by Greyson Parrelli
parent ab56856f41
commit c1b54b3532
16 changed files with 389 additions and 361 deletions

View file

@ -160,8 +160,8 @@ class AvatarPickerFragment : Fragment(R.layout.avatar_picker_fragment) {
val menuRes = when (avatar) {
is Avatar.Photo -> R.menu.avatar_picker_context
is Avatar.Text -> R.menu.avatar_picker_context
is Avatar.Vector -> return false
is Avatar.Resource -> return false
is Avatar.Vector -> return true
is Avatar.Resource -> return true
}
val popup = PopupMenu(context, anchorView, Gravity.TOP)

View file

@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.avatar.picker
import android.util.TypedValue
import android.view.View
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
@ -57,15 +58,18 @@ object AvatarPickerItem {
init {
textView.typeface = AvatarRenderer.getTypeface(context)
textView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
updateAndApplyText(textView.text.toString())
updateFontSize(textView.text.toString())
}
}
private fun updateAndApplyText(text: String) {
private fun updateFontSize(text: String) {
val textSize = Avatars.getTextSizeForLength(context, text, textView.measuredWidth * 0.8f, textView.measuredHeight * 0.45f)
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
if (textView !is EditText) {
textView.text = text
}
}
override fun bind(model: Model) {
val alpha = if (model.isSelected) 1f else 0f
@ -76,13 +80,9 @@ object AvatarPickerItem {
selectedOverlay?.animate()?.cancel()
selectedFader?.animate()?.cancel()
if (model.isSelected) {
itemView.setOnLongClickListener {
onAvatarLongClickListener?.invoke(itemView, model.avatar) ?: false
}
} else {
itemView.setOnLongClickListener(null)
}
itemView.setOnClickListener { onAvatarClickListener?.invoke(model.avatar, model.isSelected) }
@ -108,7 +108,10 @@ object AvatarPickerItem {
is Avatar.Text -> {
textView.visible = true
updateAndApplyText(model.avatar.text)
updateFontSize(model.avatar.text)
if (textView.text.toString() != model.avatar.text) {
textView.text = model.avatar.text
}
imageView.setImageDrawable(null)
imageView.background.colorFilter = SimpleColorFilter(model.avatar.color.backgroundColor)

View file

@ -31,12 +31,13 @@ import org.thoughtcrime.securesms.util.ViewUtil
/**
* Fragment to create an avatar based off of a Vector or Text (via a pager)
*/
class TextAvatarCreationFragment : Fragment(R.layout.text_avatar_creation_fragment_hidden_recycler) {
class TextAvatarCreationFragment : Fragment(R.layout.text_avatar_creation_fragment) {
private val viewModel: TextAvatarCreationViewModel by viewModels(factoryProducer = this::createFactory)
private lateinit var textInput: EditText
private lateinit var recycler: RecyclerView
private lateinit var content: ConstraintLayout
private val withRecyclerSet = ConstraintSet()
private val withoutRecyclerSet = ConstraintSet()
@ -60,9 +61,10 @@ class TextAvatarCreationFragment : Fragment(R.layout.text_avatar_creation_fragme
val tabLayout: ControllableTabLayout = view.findViewById(R.id.text_avatar_creation_tabs)
val doneButton: View = view.findViewById(R.id.text_avatar_creation_done)
withRecyclerSet.load(requireContext(), R.layout.text_avatar_creation_fragment)
withoutRecyclerSet.load(requireContext(), R.layout.text_avatar_creation_fragment_hidden_recycler)
withRecyclerSet.load(requireContext(), R.layout.text_avatar_creation_fragment_content)
withoutRecyclerSet.load(requireContext(), R.layout.text_avatar_creation_fragment_content_hidden_recycler)
content = view.findViewById(R.id.content)
recycler = view.findViewById(R.id.text_avatar_creation_recycler)
textInput = view.findViewById(R.id.avatar_picker_item_text)
@ -83,19 +85,7 @@ class TextAvatarCreationFragment : Fragment(R.layout.text_avatar_creation_fragme
val viewHolder = AvatarPickerItem.ViewHolder(view)
viewModel.state.observe(viewLifecycleOwner) { state ->
EditTextUtil.setCursorColor(textInput, state.currentAvatar.color.foregroundColor)
val hadText = textInput.length() > 0
val selectionStart = textInput.selectionStart
val selectionEnd = textInput.selectionEnd
viewHolder.bind(AvatarPickerItem.Model(state.currentAvatar, false))
textInput.post {
if (!hadText) {
textInput.setSelection(textInput.length())
} else {
textInput.setSelection(selectionStart, selectionEnd)
}
}
adapter.submitList(state.colors().map { AvatarColorItem.Model(it) })
hasBoundFromViewModel = true
@ -114,8 +104,8 @@ class TextAvatarCreationFragment : Fragment(R.layout.text_avatar_creation_fragme
}
textInput.setOnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
doneButton.performClick()
if (actionId == EditorInfo.IME_ACTION_NEXT) {
tabLayout.getTabAt(1)?.select()
true
} else {
false
@ -130,20 +120,18 @@ class TextAvatarCreationFragment : Fragment(R.layout.text_avatar_creation_fragme
textInput.isEnabled = true
ViewUtil.focusAndShowKeyboard(textInput)
val constraintLayout = requireView() as ConstraintLayout
TransitionManager.endTransitions(constraintLayout)
withoutRecyclerSet.applyTo(constraintLayout)
TransitionManager.beginDelayedTransition(constraintLayout)
TransitionManager.endTransitions(content)
withoutRecyclerSet.applyTo(content)
TransitionManager.beginDelayedTransition(content)
textInput.setSelection(textInput.length())
}
1 -> {
textInput.isEnabled = false
ViewUtil.hideKeyboard(requireContext(), textInput)
val constraintLayout = requireView() as ConstraintLayout
TransitionManager.endTransitions(constraintLayout)
withRecyclerSet.applyTo(constraintLayout)
TransitionManager.beginDelayedTransition(constraintLayout)
TransitionManager.endTransitions(content)
withRecyclerSet.applyTo(content)
TransitionManager.beginDelayedTransition(content)
}
}
}

View file

@ -14,29 +14,33 @@ public class EmojiSpan extends AnimatingImageSpan {
private final float SHIFT_FACTOR = 1.5f;
private final int size;
private final FontMetricsInt fm;
private int size;
private FontMetricsInt fontMetrics;
public EmojiSpan(@NonNull Drawable drawable, @NonNull TextView tv) {
super(drawable, tv);
fm = tv.getPaint().getFontMetricsInt();
size = fm != null ? Math.abs(fm.descent) + Math.abs(fm.ascent)
fontMetrics = tv.getPaint().getFontMetricsInt();
size = fontMetrics != null ? Math.abs(fontMetrics.descent) + Math.abs(fontMetrics.ascent)
: tv.getResources().getDimensionPixelSize(R.dimen.conversation_item_body_text_size);
getDrawable().setBounds(0, 0, size, size);
}
@Override
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
if (fm != null && this.fm != null) {
fm.ascent = this.fm.ascent;
fm.descent = this.fm.descent;
fm.top = this.fm.top;
fm.bottom = this.fm.bottom;
fm.leading = this.fm.leading;
return size;
if (fm != null && this.fontMetrics != null) {
fm.ascent = this.fontMetrics.ascent;
fm.descent = this.fontMetrics.descent;
fm.top = this.fontMetrics.top;
fm.bottom = this.fontMetrics.bottom;
fm.leading = this.fontMetrics.leading;
} else {
return super.getSize(paint, text, start, end, fm);
this.fontMetrics = paint.getFontMetricsInt();
this.size = Math.abs(this.fontMetrics.descent) + Math.abs(this.fontMetrics.ascent);
getDrawable().setBounds(0, 0, size, size);
}
return size;
}
@Override

View file

@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
@ -100,6 +101,10 @@ public class RetrieveProfileAvatarJob extends BaseJob {
try {
AvatarHelper.setAvatar(context, recipient.getId(), avatarStream);
if (recipient.isSelf()) {
SignalStore.misc().markHasEverHadAnAvatar();
}
} catch (AssertionError e) {
throw new IOException("Failed to copy stream. Likely a Conscrypt issue.", e);
}

View file

@ -14,6 +14,7 @@ public final class MiscellaneousValues extends SignalStoreValues {
private static final String USERNAME_SHOW_REMINDER = "username.show.reminder";
private static final String CLIENT_DEPRECATED = "misc.client_deprecated";
private static final String OLD_DEVICE_TRANSFER_LOCKED = "misc.old_device.transfer.locked";
private static final String HAS_EVER_HAD_AN_AVATAR = "misc.has.ever.had.an.avatar";
MiscellaneousValues(@NonNull KeyValueStore store) {
super(store);
@ -88,4 +89,12 @@ public final class MiscellaneousValues extends SignalStoreValues {
public void clearOldDeviceTransferLocked() {
putBoolean(OLD_DEVICE_TRANSFER_LOCKED, false);
}
public boolean hasEverHadAnAvatar() {
return getBoolean(HAS_EVER_HAD_AN_AVATAR, false);
}
public void markHasEverHadAnAvatar() {
putBoolean(HAS_EVER_HAD_AN_AVATAR, true);
}
}

View file

@ -107,7 +107,7 @@ public final class Megaphones {
put(Event.ONBOARDING, shouldShowOnboardingMegaphone(context) ? ALWAYS : NEVER);
put(Event.NOTIFICATIONS, shouldShowNotificationsMegaphone(context) ? RecurringSchedule.every(TimeUnit.DAYS.toMillis(30)) : NEVER);
put(Event.CHAT_COLORS, ALWAYS);
put(Event.ADD_A_PROFILE_PHOTO, shouldShowAddAProfileMegaphone(context) ? ALWAYS : NEVER);
put(Event.ADD_A_PROFILE_PHOTO, shouldShowAddAProfilePhotoMegaphone(context) ? ALWAYS : NEVER);
}};
}
@ -383,8 +383,18 @@ public final class Megaphones {
return shouldShow;
}
private static boolean shouldShowAddAProfileMegaphone(@NonNull Context context) {
return !AvatarHelper.hasAvatar(context, Recipient.self().getId());
private static boolean shouldShowAddAProfilePhotoMegaphone(@NonNull Context context) {
if (SignalStore.misc().hasEverHadAnAvatar()) {
return false;
}
boolean hasAnAvatar = AvatarHelper.hasAvatar(context, Recipient.self().getId());
if (hasAnAvatar) {
SignalStore.misc().markHasEverHadAnAvatar();
return false;
}
return true;
}
public enum Event {

View file

@ -253,7 +253,10 @@ public class EditProfileFragment extends LoggingFragment {
private void initializeProfileAvatar() {
viewModel.avatar().observe(getViewLifecycleOwner(), bytes -> {
if (bytes == null) return;
if (bytes == null) {
GlideApp.with(this).clear(avatar);
return;
}
GlideApp.with(this)
.load(bytes)

View file

@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileContentUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob;
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints;
import org.thoughtcrime.securesms.profiles.ProfileName;
@ -146,6 +147,10 @@ public class EditSelfProfileRepository implements EditProfileRepository {
RegistrationUtil.maybeMarkRegistrationComplete(context);
if (avatar != null) {
SignalStore.misc().markHasEverHadAnAvatar();
}
return UploadResult.SUCCESS;
}, uploadResultConsumer::accept);
}

View file

@ -9,6 +9,7 @@ import androidx.core.util.Consumer;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -53,6 +54,7 @@ final class ManageProfileRepository {
try {
ProfileUtil.uploadProfileWithAvatar(context, new StreamDetails(new ByteArrayInputStream(data), contentType, data.length));
AvatarHelper.setAvatar(context, Recipient.self().getId(), new ByteArrayInputStream(data));
SignalStore.misc().markHasEverHadAnAvatar();
callback.accept(Result.SUCCESS);
} catch (IOException e) {
Log.w(TAG, "Failed to upload profile during avatar change.", e);

View file

@ -5,25 +5,25 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/avatar_picker_toolbar"
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="@drawable/ic_x_tinted" />
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/avatar_picker_item_image"
android:layout_width="@dimen/avatar_picker_image_width"
android:layout_height="@dimen/avatar_picker_image_width"
android:layout_marginTop="?actionBarSize"
android:background="@drawable/circle_tintable"
android:contentDescription="@string/AvatarPickerFragment__avatar_preview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/avatar_picker_toolbar"
app:layout_constraintTop_toTopOf="parent"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Signal.Circle"
tools:backgroundTint="#DEE3FF" />
@ -115,26 +115,12 @@
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_min="160dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/avatar_picker_select_header"
app:spanCount="4"
tools:listitem="@layout/avatar_picker_item" />
<com.google.android.material.button.MaterialButton
android:id="@+id/avatar_picker_save"
style="@style/Signal.Widget.Button.Large.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:alpha="0.5"
android:enabled="false"
android:minHeight="48dp"
android:text="@string/AvatarPickerFragment__save"
app:cornerRadius="28dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/avatar_picker_clear"
android:layout_width="wrap_content"
@ -151,3 +137,31 @@
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
<androidx.appcompat.widget.Toolbar
android:id="@+id/avatar_picker_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="@drawable/ic_x_tinted" />
<com.google.android.material.button.MaterialButton
android:id="@+id/avatar_picker_save"
style="@style/Signal.Widget.Button.Large.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:alpha="0.5"
android:enabled="false"
android:minHeight="48dp"
android:text="@string/AvatarPickerFragment__save"
app:cornerRadius="28dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -17,6 +16,7 @@
app:title="@string/TextAvatarCreationFragment__preview"
app:titleTextAppearance="@style/Signal.Text.Title" />
<View
android:id="@+id/text_avatar_creation_hairline"
android:layout_width="match_parent"
@ -54,62 +54,16 @@
</org.thoughtcrime.securesms.components.ControllableTabLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/avatar_picker_image_wrapper"
android:layout_width="match_parent"
android:layout_height="240dp"
app:layout_constraintBottom_toTopOf="@id/text_avatar_creation_recycler"
app:layout_constraintTop_toBottomOf="@id/text_avatar_creation_tabs"
app:layout_constraintVertical_chainStyle="spread_inside">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/avatar_picker_item_image"
android:layout_width="@dimen/avatar_picker_image_width"
android:layout_height="@dimen/avatar_picker_image_width"
android:layout_marginTop="24dp"
android:background="@drawable/circle_tintable"
android:contentDescription="@string/AvatarPickerFragment__avatar_preview"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:backgroundTint="#DEE3FF" />
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
android:id="@+id/avatar_picker_item_text"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@null"
android:fontFamily="sans-serif-medium"
android:gravity="center"
android:inputType="textCapCharacters"
app:layout_constraintBottom_toBottomOf="@id/avatar_picker_item_image"
app:layout_constraintEnd_toEndOf="@id/avatar_picker_item_image"
app:layout_constraintStart_toStartOf="@id/avatar_picker_item_image"
app:layout_constraintTop_toTopOf="@id/avatar_picker_item_image"
tools:ignore="SpUsage"
tools:text="AF" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/text_avatar_creation_recycler"
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="@dimen/dsl_settings_gutter"
android:layout_marginTop="24dp"
android:layout_marginEnd="@dimen/dsl_settings_gutter"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingBottom="80dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
android:fillViewport="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="326dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/avatar_picker_image_wrapper"
app:spanCount="4"
tools:itemCount="12"
tools:listitem="@layout/avatar_picker_item" />
app:layout_constraintTop_toBottomOf="@id/text_avatar_creation_tabs">
<include layout="@layout/text_avatar_creation_fragment_content_hidden_recycler" />
</ScrollView>
<com.google.android.material.button.MaterialButton
android:id="@+id/text_avatar_creation_done"

View file

@ -0,0 +1,68 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/avatar_picker_image_wrapper"
android:layout_width="match_parent"
android:layout_height="240dp"
app:layout_constraintBottom_toTopOf="@id/text_avatar_creation_recycler"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="spread_inside">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/avatar_picker_item_image"
android:layout_width="@dimen/avatar_picker_image_width"
android:layout_height="@dimen/avatar_picker_image_width"
android:layout_marginTop="24dp"
android:background="@drawable/circle_tintable"
android:contentDescription="@string/AvatarPickerFragment__avatar_preview"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:backgroundTint="#DEE3FF" />
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
android:id="@+id/avatar_picker_item_text"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@null"
android:fontFamily="sans-serif-medium"
android:gravity="center"
android:imeOptions="actionNext"
android:inputType="textCapCharacters"
app:layout_constraintBottom_toBottomOf="@id/avatar_picker_item_image"
app:layout_constraintEnd_toEndOf="@id/avatar_picker_item_image"
app:layout_constraintStart_toStartOf="@id/avatar_picker_item_image"
app:layout_constraintTop_toTopOf="@id/avatar_picker_item_image"
tools:ignore="SpUsage"
tools:text="AF" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/text_avatar_creation_recycler"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="@dimen/dsl_settings_gutter"
android:layout_marginTop="24dp"
android:layout_marginEnd="@dimen/dsl_settings_gutter"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingBottom="80dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="326dp"
app:layout_constraintHeight_min="160dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/avatar_picker_image_wrapper"
app:spanCount="4"
tools:itemCount="12"
tools:listitem="@layout/avatar_picker_item" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -2,63 +2,15 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.thoughtcrime.securesms.util.views.DarkOverflowToolbar
android:id="@+id/text_avatar_creation_toolbar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:theme="?attr/settingsToolbarStyle"
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/TextAvatarCreationFragment__preview"
app:titleTextAppearance="@style/Signal.Text.Title" />
<View
android:id="@+id/text_avatar_creation_hairline"
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_marginTop="-1dp"
android:background="@color/transparent_black_05"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_avatar_creation_tabs" />
<org.thoughtcrime.securesms.components.ControllableTabLayout
android:id="@+id/text_avatar_creation_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_avatar_creation_toolbar"
app:tabGravity="fill"
app:tabIndicatorColor="@color/signal_inverse_primary"
app:tabIndicatorFullWidth="false"
app:tabMode="fixed"
app:tabTextAppearance="@style/TextAppearance.Signal.Body2">
<com.google.android.material.tabs.TabItem
android:id="@+id/text_avatar_creation_tab_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/TextAvatarCreationFragment__text" />
<com.google.android.material.tabs.TabItem
android:id="@+id/text_avatar_creation_tab_color"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/TextAvatarCreationFragment__color" />
</org.thoughtcrime.securesms.components.ControllableTabLayout>
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/avatar_picker_image_wrapper"
android:layout_width="match_parent"
android:layout_height="240dp"
app:layout_constraintTop_toBottomOf="@id/text_avatar_creation_tabs"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="spread_inside">
<androidx.appcompat.widget.AppCompatImageView
@ -81,6 +33,7 @@
android:background="@null"
android:fontFamily="sans-serif-medium"
android:gravity="center"
android:imeOptions="actionNext"
android:inputType="textCapCharacters"
app:layout_constraintBottom_toBottomOf="@id/avatar_picker_item_image"
app:layout_constraintEnd_toEndOf="@id/avatar_picker_item_image"
@ -105,23 +58,11 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="326dp"
app:layout_constraintHeight_min="160dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/avatar_picker_image_wrapper"
app:spanCount="4"
tools:itemCount="12"
tools:listitem="@layout/avatar_picker_item" />
<com.google.android.material.button.MaterialButton
android:id="@+id/text_avatar_creation_done"
style="@style/Signal.Widget.Button.Large.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:minHeight="48dp"
android:text="@string/TextAvatarCreationFragment__done"
app:cornerRadius="28dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -8,7 +8,7 @@
<org.thoughtcrime.securesms.util.views.DarkOverflowToolbar
android:id="@+id/vector_avatar_creation_toolbar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_height="?actionBarSize"
android:theme="?attr/settingsToolbarStyle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -17,12 +17,23 @@
app:title="@string/TextAvatarCreationFragment__preview"
app:titleTextAppearance="@style/Signal.Text.Title" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:fillViewport="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/vector_avatar_creation_toolbar">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/avatar_picker_image_wrapper"
android:layout_width="match_parent"
android:layout_height="240dp"
app:layout_constraintBottom_toTopOf="@id/vector_avatar_creation_divider"
app:layout_constraintTop_toBottomOf="@id/vector_avatar_creation_toolbar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="spread_inside">
<androidx.appcompat.widget.AppCompatImageView
@ -61,7 +72,7 @@
android:paddingTop="24dp"
android:paddingEnd="@dimen/dsl_settings_gutter"
android:paddingBottom="12dp"
android:text="@string/AvatarPickerFragment__select_an_avatar"
android:text="@string/VectorAvatarCreationFragment__select_a_color"
android:textAppearance="@style/TextAppearance.Signal.Body1.Bold"
app:layout_constraintBottom_toTopOf="@id/vector_avatar_creation_recycler"
app:layout_constraintEnd_toEndOf="parent"
@ -81,11 +92,15 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="326dp"
app:layout_constraintHeight_min="160dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/vector_avatar_creation_header"
app:spanCount="4"
tools:listitem="@layout/avatar_picker_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
<com.google.android.material.button.MaterialButton
android:id="@+id/vector_avatar_creation_done"
style="@style/Signal.Widget.Button.Large.Primary"

View file

@ -3626,6 +3626,8 @@
<!-- VoiceNotePlayerView -->
<string name="VoiceNotePlayerView__s_dot_s">%1$s · %2$s</string>
<!-- AvatarPickerFragment -->
<string name="AvatarPickerFragment__avatar_preview">Avatar preview</string>
<string name="AvatarPickerFragment__camera">Camera</string>
<string name="AvatarPickerFragment__take_a_picture">Take a picture</string>
@ -3634,17 +3636,22 @@
<string name="AvatarPickerFragment__text">Text</string>
<string name="AvatarPickerFragment__save">Save</string>
<string name="AvatarPickerFragment__select_an_avatar">Select an avatar</string>
<string name="AvatarPickerFragment__clear_avatar">Clear avatar</string>
<string name="AvatarPickerFragment__edit">Edit</string>
<string name="AvatarPickerRepository__failed_to_save_avatar">Failed to save avatar</string>
<!-- TextAvatarCreationFragment -->
<string name="TextAvatarCreationFragment__preview">Preview</string>
<string name="TextAvatarCreationFragment__done">Done</string>
<string name="TextAvatarCreationFragment__text">Text</string>
<string name="TextAvatarCreationFragment__color">Color</string>
<!-- VectorAvatarCreationFragment -->
<string name="VectorAvatarCreationFragment__select_a_color">Select a color</string>
<!-- ContactSelectionListItem -->
<string name="ContactSelectionListItem__sms">SMS</string>
<string name="ContactSelectionListItem__dot_s">· %1$s</string>
<string name="AvatarPickerFragment__clear_avatar">Clear avatar</string>
<string name="AvatarPickerFragment__edit">Edit</string>
<string name="AvatarPickerRepository__failed_to_save_avatar">Failed to save avatar</string>
<!-- EOF -->