diff --git a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java index a25a9891ee..de396b338d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java @@ -92,6 +92,8 @@ public interface BindableConversationItem extends Unbindable, GiphyMp4Playable, void onInMemoryMessageClicked(@NonNull InMemoryMessageRecord messageRecord); void onViewGroupDescriptionChange(@Nullable GroupId groupId, @NonNull String description, boolean isMessageRequestAccepted); void onChangeNumberUpdateContact(@NonNull Recipient recipient); + void onCallToAction(@NonNull String action); + void onDonateClicked(); /** @return true if handled, false if you want to let the normal url handling continue */ boolean onUrlClicked(@NonNull String url); diff --git a/app/src/main/java/org/thoughtcrime/securesms/BlockUnblockDialog.java b/app/src/main/java/org/thoughtcrime/securesms/BlockUnblockDialog.java index b090c8d39e..9da24e2aaa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BlockUnblockDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BlockUnblockDialog.java @@ -76,6 +76,11 @@ public final class BlockUnblockDialog { builder.setPositiveButton(R.string.RecipientPreferenceActivity_block, ((dialog, which) -> onBlock.run())); builder.setNegativeButton(android.R.string.cancel, null); } + } else if (recipient.isReleaseNotes()) { + builder.setTitle(resources.getString(R.string.BlockUnblockDialog_block_s, recipient.getDisplayName(context))); + builder.setMessage(R.string.BlockUnblockDialog_block_getting_signal_updates_and_news); + builder.setPositiveButton(R.string.BlockUnblockDialog_block, ((dialog, which) -> onBlock.run())); + builder.setNegativeButton(android.R.string.cancel, null); } else { builder.setTitle(resources.getString(R.string.BlockUnblockDialog_block_s, recipient.getDisplayName(context))); builder.setMessage(R.string.BlockUnblockDialog_blocked_people_wont_be_able_to_call_you_or_send_you_messages); @@ -115,6 +120,12 @@ public final class BlockUnblockDialog { builder.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, ((dialog, which) -> onUnblock.run())); builder.setNegativeButton(android.R.string.cancel, null); } + } else if (recipient.isReleaseNotes()) { + builder.setTitle(resources.getString(R.string.BlockUnblockDialog_unblock_s, recipient.getDisplayName(context))); + builder.setMessage(R.string.BlockUnblockDialog_resume_getting_signal_updates_and_news); + + builder.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, ((dialog, which) -> onUnblock.run())); + builder.setNegativeButton(android.R.string.cancel, null); } else { builder.setTitle(resources.getString(R.string.BlockUnblockDialog_unblock_s, recipient.getDisplayName(context))); builder.setMessage(R.string.BlockUnblockDialog_you_will_be_able_to_call_and_message_each_other); diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/Avatars.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/Avatars.kt index 4ffb9d5dad..8222e2d474 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/avatar/Avatars.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/Avatars.kt @@ -143,11 +143,10 @@ object Avatars { ) data class ColorPair( - val backgroundAvatarColor: AvatarColor, - val foregroundAvatarColor: ForegroundColor + @ColorInt val backgroundColor: Int, + @ColorInt val foregroundColor: Int, + val code: String ) { - @ColorInt val backgroundColor: Int = backgroundAvatarColor.colorInt() - @ColorInt val foregroundColor: Int = foregroundAvatarColor.colorInt - val code: String = backgroundAvatarColor.serialize() + constructor(backgroundAvatarColor: AvatarColor, foregroundAvatarColor: ForegroundColor) : this(backgroundAvatarColor.colorInt(), foregroundAvatarColor.colorInt, backgroundAvatarColor.serialize()) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java b/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java index b6899d11c2..38d5e8a2cd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/FromTextView.java @@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.components; import android.content.Context; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; -import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.SpannableString; @@ -18,6 +17,7 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.emoji.SimpleEmojiTextView; import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.ContextUtil; import org.thoughtcrime.securesms.util.SpanUtil; import org.thoughtcrime.securesms.util.ViewUtil; @@ -44,7 +44,7 @@ public class FromTextView extends SimpleEmojiTextView { } public void setText(Recipient recipient, boolean read, @Nullable String suffix) { - setText(recipient, recipient.getDisplayName(getContext()), read, suffix); + setText(recipient, recipient.getDisplayNameOrUsername(getContext()), read, suffix); } public void setText(Recipient recipient, @Nullable CharSequence fromString, boolean read, @Nullable String suffix) { @@ -62,11 +62,19 @@ public class FromTextView extends SimpleEmojiTextView { builder.append(suffix); } + if (recipient.isReleaseNotes()) { + Drawable official = ContextUtil.requireDrawable(getContext(), R.drawable.ic_official_20); + official.setBounds(0, 0, ViewUtil.dpToPx(20), ViewUtil.dpToPx(20)); + + builder.append(" ") + .append(SpanUtil.buildCenteredImageSpan(official)); + } + setText(builder); if (recipient.isBlocked()) setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_block_grey600_18dp, 0, 0, 0); else if (recipient.isMuted()) setCompoundDrawablesRelativeWithIntrinsicBounds(getMuted(), null, null, null); - else setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + else setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0); } private Drawable getMuted() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt index a4120bf0d5..f3b47ddb8d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt @@ -393,28 +393,32 @@ class ConversationSettingsFragment : DSLSettingsFragment( enabled = it.canEditGroupAttributes } - clickPref( - title = DSLSettingsText.from(R.string.ConversationSettingsFragment__disappearing_messages), - summary = summary, - icon = DSLSettingsIcon.from(icon), - isEnabled = enabled, - onClick = { - val action = ConversationSettingsFragmentDirections.actionConversationSettingsFragmentToAppSettingsExpireTimer() - .setInitialValue(state.disappearingMessagesLifespan) - .setRecipientId(state.recipient.id) - .setForResultMode(false) + if (!state.recipient.isReleaseNotes) { + clickPref( + title = DSLSettingsText.from(R.string.ConversationSettingsFragment__disappearing_messages), + summary = summary, + icon = DSLSettingsIcon.from(icon), + isEnabled = enabled, + onClick = { + val action = ConversationSettingsFragmentDirections.actionConversationSettingsFragmentToAppSettingsExpireTimer() + .setInitialValue(state.disappearingMessagesLifespan) + .setRecipientId(state.recipient.id) + .setForResultMode(false) - navController.safeNavigate(action) - } - ) + navController.safeNavigate(action) + } + ) + } - clickPref( - title = DSLSettingsText.from(R.string.preferences__chat_color_and_wallpaper), - icon = DSLSettingsIcon.from(R.drawable.ic_color_24), - onClick = { - startActivity(ChatWallpaperActivity.createIntent(requireContext(), state.recipient.id)) - } - ) + if (!state.recipient.isReleaseNotes) { + clickPref( + title = DSLSettingsText.from(R.string.preferences__chat_color_and_wallpaper), + icon = DSLSettingsIcon.from(R.drawable.ic_color_24), + onClick = { + startActivity(ChatWallpaperActivity.createIntent(requireContext(), state.recipient.id)) + } + ) + } if (!state.recipient.isSelf) { clickPref( @@ -507,7 +511,7 @@ class ConversationSettingsFragment : DSLSettingsFragment( ) } - if (recipientSettingsState.selfHasGroups) { + if (recipientSettingsState.selfHasGroups && !state.recipient.isReleaseNotes) { dividerPref() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt index 813574d772..b1c60dbae8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt @@ -130,8 +130,8 @@ sealed class ConversationSettingsViewModel( state.copy( recipient = recipient, buttonStripState = ButtonStripPreference.State( - isVideoAvailable = recipient.registered == RecipientDatabase.RegisteredState.REGISTERED && !recipient.isSelf && !recipient.isBlocked, - isAudioAvailable = !recipient.isGroup && !recipient.isSelf && !recipient.isBlocked, + isVideoAvailable = recipient.registered == RecipientDatabase.RegisteredState.REGISTERED && !recipient.isSelf && !recipient.isBlocked && !recipient.isReleaseNotes, + isAudioAvailable = !recipient.isGroup && !recipient.isSelf && !recipient.isBlocked && !recipient.isReleaseNotes, isAudioSecure = recipient.registered == RecipientDatabase.RegisteredState.REGISTERED, isMuted = recipient.isMuted, isMuteAvailable = !recipient.isSelf, @@ -141,7 +141,7 @@ sealed class ConversationSettingsViewModel( canModifyBlockedState = !recipient.isSelf && RecipientUtil.isBlockable(recipient), specificSettingsState = state.requireRecipientSettingsState().copy( contactLinkState = when { - recipient.isSelf -> ContactLinkState.NONE + recipient.isSelf || recipient.isReleaseNotes -> ContactLinkState.NONE recipient.isSystemContact -> ContactLinkState.OPEN else -> ContactLinkState.ADD } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/BioTextPreference.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/BioTextPreference.kt index 3242b99fc8..0c952dceb7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/BioTextPreference.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/BioTextPreference.kt @@ -26,8 +26,9 @@ object BioTextPreference { abstract class BioTextPreferenceModel> : PreferenceModel() { abstract fun getHeadlineText(context: Context): String - abstract fun getSubhead1Text(): String? + abstract fun getSubhead1Text(context: Context): String? abstract fun getSubhead2Text(): String? + abstract fun getCompoundDrawable(): Int } class RecipientModel( @@ -36,10 +37,20 @@ object BioTextPreference { override fun getHeadlineText(context: Context): String = recipient.getDisplayNameOrUsername(context) - override fun getSubhead1Text(): String? = recipient.combinedAboutAndEmoji + override fun getSubhead1Text(context: Context): String? { + return if (recipient.isReleaseNotes) { + context.getString(R.string.ReleaseNotes__signal_release_notes_and_news) + } else { + recipient.combinedAboutAndEmoji + } + } override fun getSubhead2Text(): String? = recipient.e164.transform(PhoneNumberFormatter::prettyPrint).orNull() + override fun getCompoundDrawable(): Int { + return if (recipient.isReleaseNotes) R.drawable.ic_official_28 else 0 + } + override fun areContentsTheSame(newItem: RecipientModel): Boolean { return super.areContentsTheSame(newItem) && newItem.recipient.hasSameContent(recipient) } @@ -55,10 +66,12 @@ object BioTextPreference { ) : BioTextPreferenceModel() { override fun getHeadlineText(context: Context): String = groupTitle - override fun getSubhead1Text(): String? = groupMembershipDescription + override fun getSubhead1Text(context: Context): String? = groupMembershipDescription override fun getSubhead2Text(): String? = null + override fun getCompoundDrawable(): Int = 0 + override fun areContentsTheSame(newItem: GroupModel): Boolean { return super.areContentsTheSame(newItem) && groupTitle == newItem.groupTitle && @@ -78,8 +91,9 @@ object BioTextPreference { override fun bind(model: T) { headline.text = model.getHeadlineText(context) + headline.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, model.getCompoundDrawable(), 0) - model.getSubhead1Text().let { + model.getSubhead1Text(context).let { subhead1.text = it subhead1.visibility = if (it == null) View.GONE else View.VISIBLE } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBannerView.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBannerView.java index b667c7d00b..274c96eddf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBannerView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBannerView.java @@ -78,11 +78,26 @@ public class ConversationBannerView extends ConstraintLayout { } } - public void setTitle(@Nullable CharSequence title) { + public String setTitle(@NonNull Recipient recipient) { + if (recipient.isReleaseNotes()) { + contactTitle.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_official_28, 0); + } else { + contactTitle.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0); + } + + String title = recipient.isSelf() ? getContext().getString(R.string.note_to_self) : recipient.getDisplayNameOrUsername(getContext()); contactTitle.setText(title); + return title; } - public void setAbout(@Nullable String about) { + public void setAbout(@NonNull Recipient recipient) { + String about; + if (recipient.isReleaseNotes()) { + about = getContext().getString(R.string.ReleaseNotes__signal_release_notes_and_news); + } else { + about = recipient.getCombinedAboutAndEmoji(); + } + contactAbout.setText(about); contactAbout.setVisibility(TextUtils.isEmpty(about) ? GONE : VISIBLE); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 69ddc068e3..b65a13d069 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -552,9 +552,8 @@ public class ConversationFragment extends LoggingFragment implements Multiselect conversationBanner.setAvatar(GlideApp.with(context), recipient); conversationBanner.showBackgroundBubble(recipient.hasWallpaper()); - String title = isSelf ? context.getString(R.string.note_to_self) : recipient.getDisplayNameOrUsername(context); - conversationBanner.setTitle(title); - conversationBanner.setAbout(recipient.getCombinedAboutAndEmoji()); + String title = conversationBanner.setTitle(recipient); + conversationBanner.setAbout(recipient); if (recipient.isGroup()) { if (pendingMemberCount > 0) { @@ -1821,6 +1820,16 @@ public class ConversationFragment extends LoggingFragment implements Multiselect public void onChangeNumberUpdateContact(@NonNull Recipient recipient) { startActivity(RecipientExporter.export(recipient).asAddContactIntent()); } + + @Override + public void onCallToAction(@NonNull String action) { + + } + + @Override + public void onDonateClicked() { + + } } public void refreshList() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index a1b06c2b23..06d036663e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -45,6 +45,7 @@ import android.view.MotionEvent; import android.view.TouchDelegate; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; @@ -122,6 +123,7 @@ import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.InterceptableLongClickCopyLinkSpan; import org.thoughtcrime.securesms.util.LongClickMovementMethod; import org.thoughtcrime.securesms.util.MessageRecordUtil; +import org.thoughtcrime.securesms.util.PlaceholderURLSpan; import org.thoughtcrime.securesms.util.Projection; import org.thoughtcrime.securesms.util.ProjectionList; import org.thoughtcrime.securesms.util.SearchUtil; @@ -199,6 +201,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private Stub linkPreviewStub; private Stub stickerStub; private Stub revealableStub; + private Stub