diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerFragment.kt
index ee0ba36a77..2fd3babe78 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerFragment.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerFragment.kt
@@ -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)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerItem.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerItem.kt
index f7ab75ff8d..2ae0745482 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerItem.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerItem.kt
@@ -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,14 +58,17 @@ 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)
- textView.text = text
+
+ if (textView !is EditText) {
+ textView.text = text
+ }
}
override fun bind(model: Model) {
@@ -76,12 +80,8 @@ object AvatarPickerItem {
selectedOverlay?.animate()?.cancel()
selectedFader?.animate()?.cancel()
- if (model.isSelected) {
- itemView.setOnLongClickListener {
- onAvatarLongClickListener?.invoke(itemView, model.avatar) ?: false
- }
- } else {
- itemView.setOnLongClickListener(null)
+ itemView.setOnLongClickListener {
+ onAvatarLongClickListener?.invoke(itemView, model.avatar) ?: false
}
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)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/text/TextAvatarCreationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/text/TextAvatarCreationFragment.kt
index 15aa885833..c991536f6c 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/avatar/text/TextAvatarCreationFragment.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/text/TextAvatarCreationFragment.kt
@@ -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)
}
}
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java
index 1733cb5cc2..a0e34e476b 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java
@@ -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)
- : tv.getResources().getDimensionPixelSize(R.dimen.conversation_item_body_text_size);
+ 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
diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
index 8dfa090a1a..a658081429 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
@@ -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);
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java
index bae602f36c..a9cc647138 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java
@@ -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);
+ }
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java
index a4529178cf..f2c87cae2b 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java
@@ -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 {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileFragment.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileFragment.java
index ded430643d..3558b4c011 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileFragment.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileFragment.java
@@ -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)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditSelfProfileRepository.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditSelfProfileRepository.java
index 0e02f9640f..182179a3e6 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditSelfProfileRepository.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditSelfProfileRepository.java
@@ -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);
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/ManageProfileRepository.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/ManageProfileRepository.java
index 7eef53aa79..10863ddb8a 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/ManageProfileRepository.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/ManageProfileRepository.java
@@ -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);
diff --git a/app/src/main/res/layout/avatar_picker_fragment.xml b/app/src/main/res/layout/avatar_picker_fragment.xml
index bfb2f7d7bb..2dc4005a8e 100644
--- a/app/src/main/res/layout/avatar_picker_fragment.xml
+++ b/app/src/main/res/layout/avatar_picker_fragment.xml
@@ -5,6 +5,140 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/text_avatar_creation_fragment.xml b/app/src/main/res/layout/text_avatar_creation_fragment.xml
index 08cbec917d..d5fc9a9c25 100644
--- a/app/src/main/res/layout/text_avatar_creation_fragment.xml
+++ b/app/src/main/res/layout/text_avatar_creation_fragment.xml
@@ -1,7 +1,6 @@
@@ -17,6 +16,7 @@
app:title="@string/TextAvatarCreationFragment__preview"
app:titleTextAppearance="@style/Signal.Text.Title" />
+
-
-
-
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@id/text_avatar_creation_tabs">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/text_avatar_creation_fragment_hidden_recycler.xml b/app/src/main/res/layout/text_avatar_creation_fragment_content_hidden_recycler.xml
similarity index 51%
rename from app/src/main/res/layout/text_avatar_creation_fragment_hidden_recycler.xml
rename to app/src/main/res/layout/text_avatar_creation_fragment_content_hidden_recycler.xml
index b91bce69ea..c592549fa8 100644
--- a/app/src/main/res/layout/text_avatar_creation_fragment_hidden_recycler.xml
+++ b/app/src/main/res/layout/text_avatar_creation_fragment_content_hidden_recycler.xml
@@ -2,63 +2,15 @@
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="wrap_content">
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/vector_avatar_creation_fragment.xml b/app/src/main/res/layout/vector_avatar_creation_fragment.xml
index a5ad0c5e66..55816e7fa0 100644
--- a/app/src/main/res/layout/vector_avatar_creation_fragment.xml
+++ b/app/src/main/res/layout/vector_avatar_creation_fragment.xml
@@ -8,7 +8,7 @@
-
-
-
-
-
-
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@id/vector_avatar_creation_toolbar">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
%1$s · %2$s
+
+
Avatar preview
Camera
Take a picture
@@ -3634,17 +3636,22 @@
Text
Save
Select an avatar
+ Clear avatar
+ Edit
+ Failed to save avatar
+
+
Preview
Done
Text
Color
+
+ Select a color
+
SMS
· %1$s
- Clear avatar
- Edit
- Failed to save avatar