diff --git a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java index 78bee7f823..618c19d3c2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java @@ -36,7 +36,8 @@ public interface BindableConversationItem extends Unbindable { @NonNull Set batchSelected, @NonNull Recipient recipients, @Nullable String searchQuery, - boolean pulseMention); + boolean pulseMention, + boolean hasWallpaper); ConversationMessage getConversationMessage(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java index dde79a526e..3aeff4fad6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java @@ -12,6 +12,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -130,6 +131,20 @@ public class ConversationItemFooter extends LinearLayout { presentDeliveryStatus(messageRecord); } + public void enableBubbleBackground(@DrawableRes int drawableRes, @Nullable Integer tint) { + setBackgroundResource(drawableRes); + + if (tint != null) { + getBackground().setColorFilter(tint, PorterDuff.Mode.MULTIPLY); + } else { + getBackground().clearColorFilter(); + } + } + + public void disableBubbleBackground() { + setBackground(null); + } + private void presentDate(@NonNull MessageRecord messageRecord, @NonNull Locale locale) { dateView.forceLayout(); if (messageRecord.isFailed()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index fa1ddaeceb..c2a8ef6157 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -61,6 +61,7 @@ import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.widget.Button; import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; @@ -258,6 +259,7 @@ import org.thoughtcrime.securesms.util.DynamicDarkToolbarTheme; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.FeatureFlags; +import org.thoughtcrime.securesms.util.FullscreenHelper; import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MessageUtil; @@ -267,6 +269,7 @@ import org.thoughtcrime.securesms.util.SmsUtil; import org.thoughtcrime.securesms.util.SpanUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences.MediaKeyboardMode; +import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.WindowUtil; @@ -275,6 +278,8 @@ import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; import org.thoughtcrime.securesms.util.concurrent.SettableFuture; import org.thoughtcrime.securesms.util.concurrent.SimpleTask; import org.thoughtcrime.securesms.util.views.Stub; +import org.thoughtcrime.securesms.wallpaper.ChatWallpaper; +import org.thoughtcrime.securesms.wallpaper.ChatWallpaperDimLevelUtil; import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; @@ -375,6 +380,8 @@ public class ConversationActivity extends PassphraseRequiredActivity private Stub mentionsSuggestions; private MaterialButton joinGroupCallButton; private boolean callingTooltipShown; + private ImageView wallpaper; + private View wallpaperDim; private LinkPreviewViewModel linkPreviewViewModel; private ConversationSearchViewModel searchViewModel; @@ -414,6 +421,8 @@ public class ConversationActivity extends PassphraseRequiredActivity return; } + new FullscreenHelper(this).showSystemUI(); + ConversationIntents.Args args = ConversationIntents.Args.from(getIntent()); reportShortcutLaunch(args.getRecipientId()); @@ -426,6 +435,7 @@ public class ConversationActivity extends PassphraseRequiredActivity initializeReceivers(); initializeActionBar(); initializeViews(); + updateWallpaper(args.getWallpaper()); initializeResources(args); initializeLinkPreviewObserver(); initializeSearchObserver(); @@ -1910,6 +1920,8 @@ public class ConversationActivity extends PassphraseRequiredActivity messageRequestBottomView = findViewById(R.id.conversation_activity_message_request_bottom_bar); reactionOverlay = findViewById(R.id.conversation_reaction_scrubber); mentionsSuggestions = ViewUtil.findStubById(this, R.id.conversation_mention_suggestions_stub); + wallpaper = findViewById(R.id.conversation_wallpaper); + wallpaperDim = findViewById(R.id.conversation_wallpaper_dim); ImageButton quickCameraToggle = findViewById(R.id.quick_camera_toggle); ImageButton inlineAttachmentButton = findViewById(R.id.inline_attachment_button); @@ -1976,6 +1988,16 @@ public class ConversationActivity extends PassphraseRequiredActivity joinGroupCallButton.setOnClickListener(v -> handleVideo(getRecipient())); } + private void updateWallpaper(@Nullable ChatWallpaper chatWallpaper) { + if (chatWallpaper != null) { + chatWallpaper.loadInto(wallpaper); + ChatWallpaperDimLevelUtil.applyDimLevelForNightMode(wallpaperDim, chatWallpaper); + } else { + wallpaper.setImageDrawable(null); + wallpaperDim.setVisibility(View.GONE); + } + } + protected void initializeActionBar() { Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); @@ -2268,6 +2290,7 @@ public class ConversationActivity extends PassphraseRequiredActivity updateReminders(); updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId()); initializeSecurity(isSecureText, isDefaultSms); + updateWallpaper(recipient.getWallpaper()); if (searchViewItem == null || !searchViewItem.isActionViewExpanded()) { invalidateOptionsMenu(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java index 1fed09f733..77130b3627 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationAdapter.java @@ -23,6 +23,7 @@ import android.widget.FrameLayout; import android.widget.TextView; import androidx.annotation.AnyThread; +import androidx.annotation.DrawableRes; import androidx.annotation.LayoutRes; import androidx.annotation.MainThread; import androidx.annotation.NonNull; @@ -241,7 +242,8 @@ public class ConversationAdapter selected, recipient, searchQuery, - conversationMessage == recordToPulse); + conversationMessage == recordToPulse, + recipient.hasWallpaper()); if (conversationMessage == recordToPulse) { recordToPulse = null; @@ -296,6 +298,12 @@ public class ConversationAdapter public void onBindHeaderViewHolder(StickyHeaderViewHolder viewHolder, int position) { ConversationMessage conversationMessage = Objects.requireNonNull(getItem(position)); viewHolder.setText(DateUtils.getRelativeDate(viewHolder.itemView.getContext(), locale, conversationMessage.getMessageRecord().getDateReceived())); + + if (recipient.hasWallpaper()) { + viewHolder.setBackgroundRes(R.drawable.wallpaper_bubble_background_8); + } else { + viewHolder.clearBackground(); + } } public @Nullable ConversationMessage getItem(int position) { @@ -325,6 +333,12 @@ public class ConversationAdapter void onBindLastSeenViewHolder(StickyHeaderViewHolder viewHolder, int position) { viewHolder.setText(viewHolder.itemView.getContext().getResources().getQuantityString(R.plurals.ConversationAdapter_n_unread_messages, (position + 1), (position + 1))); + + if (recipient.hasWallpaper()) { + viewHolder.setBackgroundRes(R.drawable.wallpaper_bubble_background_8); + } else { + viewHolder.clearBackground(); + } } boolean hasNoConversationMessages() { @@ -563,6 +577,14 @@ public class ConversationAdapter public void setText(CharSequence text) { textView.setText(text); } + + public void setBackgroundRes(@DrawableRes int resId) { + textView.setBackgroundResource(resId); + } + + public void clearBackground() { + textView.setBackground(null); + } } private static class HeaderFooterViewHolder extends RecyclerView.ViewHolder { 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 3f63840908..96132bf7c9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBannerView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBannerView.java @@ -62,6 +62,14 @@ public class ConversationBannerView extends ConstraintLayout { contactDescription.setText(description); } + public void showBackgroundBubble(boolean enabled) { + if (enabled) { + setBackgroundResource(R.drawable.wallpaper_bubble_background_12); + } else { + setBackground(null); + } + } + public void hideSubtitle() { contactSubtitle.setVisibility(View.GONE); } 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 4a4cc3b781..f979374bf3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -422,6 +422,7 @@ public class ConversationFragment extends LoggingFragment { if (recipient != null) { 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); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationIntents.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationIntents.java index 6c20311045..ec0cace41f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationIntents.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationIntents.java @@ -9,8 +9,10 @@ import androidx.annotation.Nullable; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.mediasend.Media; +import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.stickers.StickerLocator; +import org.thoughtcrime.securesms.wallpaper.ChatWallpaper; import java.util.ArrayList; import java.util.Collection; @@ -147,6 +149,11 @@ public class ConversationIntents { public boolean isFirstTimeInSelfCreatedGroup() { return firstTimeInSelfCreatedGroup; } + + public @Nullable ChatWallpaper getWallpaper() { + // TODO [greyson][wallpaper] Is it worth it to do this beforehand? + return Recipient.resolved(recipientId).getWallpaper(); + } } public final static class Builder { 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 3b6833d2fa..d498333c96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -155,7 +155,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private boolean groupThread; private LiveRecipient recipient; private GlideRequests glideRequests; - private ValueAnimator pulseOutlinerAlphaAnimator; + private ValueAnimator pulseOutlinerAlphaAnimator; protected ConversationItemBodyBubble bodyBubble; protected View reply; @@ -165,7 +165,6 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private ConversationItemFooter footer; private ConversationItemFooter stickerFooter; @Nullable private TextView groupSender; - @Nullable private TextView groupSenderProfileName; @Nullable private View groupSenderHolder; private AvatarImageView contactPhoto; private AlertView alertView; @@ -223,7 +222,6 @@ public final class ConversationItem extends RelativeLayout implements BindableCo this.footer = findViewById(R.id.conversation_item_footer); this.stickerFooter = findViewById(R.id.conversation_item_sticker_footer); this.groupSender = findViewById(R.id.group_message_sender); - this.groupSenderProfileName = findViewById(R.id.group_message_sender_profile); this.alertView = findViewById(R.id.indicators_parent); this.contactPhoto = findViewById(R.id.contact_photo); this.contactPhotoHolder = findViewById(R.id.contact_photo_container); @@ -256,7 +254,8 @@ public final class ConversationItem extends RelativeLayout implements BindableCo @NonNull Set batchSelected, @NonNull Recipient conversationRecipient, @Nullable String searchQuery, - boolean pulse) + boolean pulse, + boolean hasWallpaper) { if (this.recipient != null) this.recipient.removeForeverObserver(this); if (this.conversationRecipient != null) this.conversationRecipient.removeForeverObserver(this); @@ -279,17 +278,17 @@ public final class ConversationItem extends RelativeLayout implements BindableCo setMessageShape(messageRecord, previousMessageRecord, nextMessageRecord, groupThread); setMediaAttributes(messageRecord, previousMessageRecord, nextMessageRecord, conversationRecipient, groupThread); setBodyText(messageRecord, searchQuery); - setBubbleState(messageRecord); + setBubbleState(messageRecord, hasWallpaper); setInteractionState(conversationMessage, pulse); setStatusIcons(messageRecord); setContactPhoto(recipient.get()); setGroupMessageStatus(messageRecord, recipient.get()); - setGroupAuthorColor(messageRecord); - setAuthor(messageRecord, previousMessageRecord, nextMessageRecord, groupThread); + setGroupAuthorColor(messageRecord, hasWallpaper); + setAuthor(messageRecord, previousMessageRecord, nextMessageRecord, groupThread, hasWallpaper); setQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread); setMessageSpacing(context, messageRecord, previousMessageRecord, nextMessageRecord, groupThread); setReactions(messageRecord); - setFooter(messageRecord, nextMessageRecord, locale, groupThread); + setFooter(messageRecord, nextMessageRecord, locale, groupThread, hasWallpaper); } @Override @@ -344,12 +343,14 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } } - ConversationItemFooter activeFooter = getActiveFooter(messageRecord); - int availableWidth = getAvailableMessageBubbleWidth(footer); + if (!hasNoBubble(messageRecord)) { + ConversationItemFooter activeFooter = getActiveFooter(messageRecord); + int availableWidth = getAvailableMessageBubbleWidth(footer); - if (activeFooter.getVisibility() != GONE && activeFooter.getMeasuredWidth() != availableWidth) { - activeFooter.getLayoutParams().width = availableWidth; - needsMeasure = true; + if (activeFooter.getVisibility() != GONE && activeFooter.getMeasuredWidth() != availableWidth) { + activeFooter.getLayoutParams().width = availableWidth; + needsMeasure = true; + } } if (needsMeasure) { @@ -366,7 +367,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo @Override public void onRecipientChanged(@NonNull Recipient modified) { - setBubbleState(messageRecord); + setBubbleState(messageRecord, modified.hasWallpaper()); if (recipient.getId().equals(modified.getId())) { setContactPhoto(modified); setGroupMessageStatus(messageRecord, modified); @@ -409,16 +410,20 @@ public final class ConversationItem extends RelativeLayout implements BindableCo /// MessageRecord Attribute Parsers - private void setBubbleState(MessageRecord messageRecord) { + private void setBubbleState(MessageRecord messageRecord, boolean hasWallpaper) { if (messageRecord.isOutgoing() && !messageRecord.isRemoteDelete()) { bodyBubble.getBackground().setColorFilter(defaultBubbleColor, PorterDuff.Mode.MULTIPLY); footer.setTextColor(ContextCompat.getColor(context, R.color.signal_text_secondary)); footer.setIconColor(ContextCompat.getColor(context, R.color.signal_icon_tint_secondary)); footer.setOnlyShowSendingStatus(false, messageRecord); } else if (messageRecord.isRemoteDelete() || (isViewOnceMessage(messageRecord) && ViewOnceUtil.isViewed((MmsMessageRecord) messageRecord))) { - bodyBubble.getBackground().setColorFilter(ContextCompat.getColor(context, R.color.signal_background_primary), PorterDuff.Mode.MULTIPLY); + if (hasWallpaper) { + bodyBubble.getBackground().setColorFilter(ContextCompat.getColor(context, R.color.wallpaper_bubble_color), PorterDuff.Mode.SRC_IN); + } else { + bodyBubble.getBackground().setColorFilter(ContextCompat.getColor(context, R.color.signal_background_primary), PorterDuff.Mode.MULTIPLY); + footer.setIconColor(ContextCompat.getColor(context, R.color.signal_icon_tint_secondary)); + } footer.setTextColor(ContextCompat.getColor(context, R.color.signal_text_secondary)); - footer.setIconColor(ContextCompat.getColor(context, R.color.signal_icon_tint_secondary)); footer.setOnlyShowSendingStatus(messageRecord.isRemoteDelete(), messageRecord); } else { bodyBubble.getBackground().setColorFilter(messageRecord.getRecipient().getColor().toConversationColor(context), PorterDuff.Mode.MULTIPLY); @@ -433,7 +438,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo pulseOutliner.setStrokeWidth(ViewUtil.dpToPx(4)); outliners.clear(); - if (shouldDrawBodyBubbleOutline(messageRecord)) { + if (shouldDrawBodyBubbleOutline(messageRecord, hasWallpaper)) { outliners.add(outliner); } outliners.add(pulseOutliner); @@ -515,9 +520,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo pulseOutliner.setAlpha(0); } - private boolean shouldDrawBodyBubbleOutline(MessageRecord messageRecord) { - boolean isIncomingViewedOnce = !messageRecord.isOutgoing() && isViewOnceMessage(messageRecord) && ViewOnceUtil.isViewed((MmsMessageRecord) messageRecord); - return isIncomingViewedOnce || messageRecord.isRemoteDelete(); + private boolean shouldDrawBodyBubbleOutline(MessageRecord messageRecord, boolean hasWallpaper) { + if (hasWallpaper) { + return false; + } else { + boolean isIncomingViewedOnce = !messageRecord.isOutgoing() && isViewOnceMessage(messageRecord) && ViewOnceUtil.isViewed((MmsMessageRecord) messageRecord); + return isIncomingViewedOnce || messageRecord.isRemoteDelete(); + } } private boolean isCaptionlessMms(MessageRecord messageRecord) { @@ -543,6 +552,10 @@ public final class ConversationItem extends RelativeLayout implements BindableCo ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide().isBorderless(); } + private boolean hasNoBubble(MessageRecord messageRecord) { + return hasSticker(messageRecord) || isBorderless(messageRecord); + } + private boolean hasOnlyThumbnail(MessageRecord messageRecord) { return hasThumbnail(messageRecord) && !hasAudio(messageRecord) && @@ -1074,7 +1087,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo }); } - private void setFooter(@NonNull MessageRecord current, @NonNull Optional next, @NonNull Locale locale, boolean isGroupThread) { + private void setFooter(@NonNull MessageRecord current, @NonNull Optional next, @NonNull Locale locale, boolean isGroupThread, boolean hasWallpaper) { ViewUtil.updateLayoutParams(footer, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); footer.setVisibility(GONE); @@ -1090,11 +1103,27 @@ public final class ConversationItem extends RelativeLayout implements BindableCo ConversationItemFooter activeFooter = getActiveFooter(current); activeFooter.setVisibility(VISIBLE); activeFooter.setMessageRecord(current, locale); + + if (hasWallpaper && hasNoBubble((messageRecord))) { + if (messageRecord.isOutgoing()) { + activeFooter.enableBubbleBackground(R.drawable.wallpaper_bubble_background_tintable_11, defaultBubbleColor); + } else { + activeFooter.enableBubbleBackground(R.drawable.wallpaper_bubble_background_tintable_11, messageRecord.getRecipient().getColor().toConversationColor(context)); + activeFooter.setTextColor(ContextCompat.getColor(context, R.color.conversation_item_received_text_secondary_color)); + activeFooter.setIconColor(ContextCompat.getColor(context, R.color.conversation_item_received_text_secondary_color)); + } + } else if (hasNoBubble(messageRecord)){ + activeFooter.disableBubbleBackground(); + activeFooter.setTextColor(ContextCompat.getColor(context, R.color.signal_text_secondary)); + activeFooter.setIconColor(ContextCompat.getColor(context, R.color.signal_icon_tint_secondary)); + } else { + activeFooter.disableBubbleBackground(); + } } } private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) { - if (hasSticker(messageRecord) || isBorderless(messageRecord)) { + if (hasNoBubble(messageRecord)) { return stickerFooter; } else if (hasSharedContact(messageRecord) && TextUtils.isEmpty(messageRecord.getDisplayBody(getContext()))) { return sharedContactStub.get().getFooter(); @@ -1118,30 +1147,27 @@ public final class ConversationItem extends RelativeLayout implements BindableCo @SuppressLint("SetTextI18n") private void setGroupMessageStatus(MessageRecord messageRecord, Recipient recipient) { - if (groupThread && !messageRecord.isOutgoing() && groupSender != null && groupSenderProfileName != null) { + if (groupThread && !messageRecord.isOutgoing() && groupSender != null) { groupSender.setText(recipient.getDisplayName(getContext())); - groupSenderProfileName.setVisibility(View.GONE); } } - private void setGroupAuthorColor(@NonNull MessageRecord messageRecord) { - if (groupSender != null && groupSenderProfileName != null) { + private void setGroupAuthorColor(@NonNull MessageRecord messageRecord, boolean hasWallpaper) { + if (groupSender != null) { int stickerAuthorColor = ContextCompat.getColor(context, R.color.signal_text_primary); - if (shouldDrawBodyBubbleOutline(messageRecord)) { + + if (shouldDrawBodyBubbleOutline(messageRecord, hasWallpaper)) { groupSender.setTextColor(stickerAuthorColor); - groupSenderProfileName.setTextColor(stickerAuthorColor); - } else if (hasSticker(messageRecord) || isBorderless(messageRecord)) { + } else if (!hasWallpaper && hasNoBubble(messageRecord)) { groupSender.setTextColor(stickerAuthorColor); - groupSenderProfileName.setTextColor(stickerAuthorColor); } else { groupSender.setTextColor(ContextCompat.getColor(context, R.color.conversation_item_received_text_primary_color)); - groupSenderProfileName.setTextColor(ContextCompat.getColor(context, R.color.conversation_item_received_text_primary_color)); } } } @SuppressWarnings("ConstantConditions") - private void setAuthor(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { + private void setAuthor(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread, boolean hasWallpaper) { if (isGroupThread && !current.isOutgoing()) { contactPhotoHolder.setVisibility(VISIBLE); @@ -1149,6 +1175,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo !DateUtils.isSameDay(previous.get().getTimestamp(), current.getTimestamp())) { groupSenderHolder.setVisibility(VISIBLE); + + if (hasWallpaper && hasNoBubble(current)) { + groupSenderHolder.setBackgroundResource(R.drawable.wallpaper_bubble_background_tintable_11); + groupSenderHolder.getBackground().setColorFilter(messageRecord.getRecipient().getColor().toConversationColor(context), PorterDuff.Mode.MULTIPLY); + } else { + groupSenderHolder.setBackground(null); + } } else { groupSenderHolder.setVisibility(GONE); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index 7ce12ebf1e..9bbc9a419c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -5,7 +5,7 @@ import android.text.Spannable; import android.text.SpannableString; import android.util.AttributeSet; import android.view.View; -import android.widget.LinearLayout; +import android.widget.FrameLayout; import android.widget.TextView; import androidx.annotation.NonNull; @@ -41,7 +41,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; -public final class ConversationUpdateItem extends LinearLayout +public final class ConversationUpdateItem extends FrameLayout implements BindableConversationItem { private static final String TAG = ConversationUpdateItem.class.getSimpleName(); @@ -50,6 +50,7 @@ public final class ConversationUpdateItem extends LinearLayout private TextView body; private TextView actionButton; + private View background; private ConversationMessage conversationMessage; private Recipient conversationRecipient; private Optional nextMessageRecord; @@ -76,6 +77,7 @@ public final class ConversationUpdateItem extends LinearLayout super.onFinishInflate(); this.body = findViewById(R.id.conversation_update_body); this.actionButton = findViewById(R.id.conversation_update_action); + this.background = findViewById(R.id.conversation_update_background); this.setOnClickListener(new InternalClickListener(null)); } @@ -90,11 +92,12 @@ public final class ConversationUpdateItem extends LinearLayout @NonNull Set batchSelected, @NonNull Recipient conversationRecipient, @Nullable String searchQuery, - boolean pulseMention) + boolean pulseMention, + boolean hasWallpaper) { this.batchSelected = batchSelected; - bind(lifecycleOwner, conversationMessage, nextMessageRecord, conversationRecipient); + bind(lifecycleOwner, conversationMessage, nextMessageRecord, conversationRecipient, hasWallpaper); } @Override @@ -110,7 +113,8 @@ public final class ConversationUpdateItem extends LinearLayout private void bind(@NonNull LifecycleOwner lifecycleOwner, @NonNull ConversationMessage conversationMessage, @NonNull Optional nextMessageRecord, - @NonNull Recipient conversationRecipient) + @NonNull Recipient conversationRecipient, + boolean hasWallpaper) { this.conversationMessage = conversationMessage; this.messageRecord = conversationMessage.getMessageRecord(); @@ -125,6 +129,12 @@ public final class ConversationUpdateItem extends LinearLayout groupObserver.observe(lifecycleOwner, null); } + if (hasWallpaper) { + background.setBackgroundResource(R.drawable.wallpaper_bubble_background_12); + } else { + background.setBackground(null); + } + UpdateDescription updateDescription = Objects.requireNonNull(messageRecord.getUpdateDisplayBody(getContext())); LiveData liveUpdateMessage = LiveUpdateMessage.fromMessageDescription(getContext(), updateDescription, ContextCompat.getColor(getContext(), R.color.conversation_item_update_text_color)); LiveData spannableMessage = loading(liveUpdateMessage); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java index d761731666..69c5b8ad24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -359,7 +359,7 @@ public class RecipientDatabase extends Database { LAST_GV1_MIGRATE_REMINDER + " INTEGER DEFAULT 0, " + LAST_SESSION_RESET + " BLOB DEFAULT NULL, " + WALLPAPER + " BLOB DEFAULT NULL, " + - WALLPAPER_URI + " TEXT DEFAULT NULL);"; + WALLPAPER_URI + " TEXT DEFAULT NULL);"; private static final String INSIGHTS_INVITEE_LIST = "SELECT " + TABLE_NAME + "." + ID + " FROM " + TABLE_NAME + @@ -1799,14 +1799,60 @@ public class RecipientDatabase extends Database { } } - public void setWallpaper(@NonNull RecipientId id, @NonNull ChatWallpaper chatWallpaper) { - Wallpaper wallpaper = chatWallpaper.serialize(); - Uri existingWallpaperUri = getWallpaperUri(id); + public void resetAllWallpaper() { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + String[] selection = SqlUtil.buildArgs(ID, WALLPAPER_URI); + String where = WALLPAPER + " IS NOT NULL"; + List> idWithWallpaper = new LinkedList<>(); + + database.beginTransaction(); + + try { + try (Cursor cursor = database.query(TABLE_NAME, selection, where, null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + idWithWallpaper.add(new Pair<>(RecipientId.from(CursorUtil.requireInt(cursor, ID)), + CursorUtil.getString(cursor, WALLPAPER_URI).orNull())); + } + } + + if (idWithWallpaper.isEmpty()) { + return; + } + + ContentValues values = new ContentValues(2); + values.put(WALLPAPER_URI, (String) null); + values.put(WALLPAPER, (byte[]) null); + + int rowsUpdated = database.update(TABLE_NAME, values, where, null); + if (rowsUpdated == idWithWallpaper.size()) { + for (Pair pair : idWithWallpaper) { + Recipient.live(pair.first()).refresh(); + if (pair.second() != null) { + WallpaperStorage.onWallpaperDeselected(context, Uri.parse(pair.second())); + } + } + } else { + throw new AssertionError("expected " + idWithWallpaper.size() + " but got " + rowsUpdated); + } + + } finally { + database.setTransactionSuccessful(); + database.endTransaction(); + } + + } + + public void setWallpaper(@NonNull RecipientId id, @Nullable ChatWallpaper chatWallpaper) { + setWallpaper(id, chatWallpaper != null ? chatWallpaper.serialize() : null); + } + + private void setWallpaper(@NonNull RecipientId id, @Nullable Wallpaper wallpaper) { + Uri existingWallpaperUri = getWallpaperUri(id); ContentValues values = new ContentValues(); - values.put(WALLPAPER, wallpaper.toByteArray()); + values.put(WALLPAPER, wallpaper != null ? wallpaper.toByteArray() : null); - if (wallpaper.hasFile()) { + if (wallpaper != null && wallpaper.hasFile()) { values.put(WALLPAPER_URI, wallpaper.getFile().getUri()); } else { values.putNull(WALLPAPER_URI); @@ -1821,15 +1867,33 @@ public class RecipientDatabase extends Database { } } - private @Nullable Uri getWallpaperUri(@NonNull RecipientId id) { + public void setDimWallpaperInDarkTheme(@NonNull RecipientId id, boolean enabled) { + Wallpaper wallpaper = getWallpaper(id); + + if (wallpaper == null) { + throw new IllegalStateException("No wallpaper set for " + id); + } + + Wallpaper updated = wallpaper.toBuilder() + .setDimLevelInDarkTheme(enabled ? ChatWallpaper.FIXED_DIM_LEVEL_FOR_DARK_THEME : 0) + .build(); + + setWallpaper(id, updated); + } + + private @Nullable Wallpaper getWallpaper(@NonNull RecipientId id) { SQLiteDatabase db = databaseHelper.getReadableDatabase(); - try (Cursor cursor = db.query(TABLE_NAME, new String[] {WALLPAPER_URI}, ID_WHERE, SqlUtil.buildArgs(id), null, null, null)) { + try (Cursor cursor = db.query(TABLE_NAME, new String[] {WALLPAPER}, ID_WHERE, SqlUtil.buildArgs(id), null, null, null)) { if (cursor.moveToFirst()) { - String raw = CursorUtil.requireString(cursor, WALLPAPER_URI); + byte[] raw = CursorUtil.requireBlob(cursor, WALLPAPER); if (raw != null) { - return Uri.parse(raw); + try { + return Wallpaper.parseFrom(raw); + } catch (InvalidProtocolBufferException e) { + return null; + } } else { return null; } @@ -1839,12 +1903,22 @@ public class RecipientDatabase extends Database { return null; } + private @Nullable Uri getWallpaperUri(@NonNull RecipientId id) { + Wallpaper wallpaper = getWallpaper(id); + + if (wallpaper != null && wallpaper.hasFile()) { + return Uri.parse(wallpaper.getFile().getUri()); + } else { + return null; + } + } + public int getWallpaperUriUsageCount(@NonNull Uri uri) { SQLiteDatabase db = databaseHelper.getReadableDatabase(); String query = WALLPAPER_URI + " = ?"; String[] args = SqlUtil.buildArgs(uri); - try (Cursor cursor = db.query(TABLE_NAME, new String[] { "COUNT(*)"}, query, args, null, null, null)) { + try (Cursor cursor = db.query(TABLE_NAME, new String[] { "COUNT(*)" }, query, args, null, null, null)) { if (cursor.moveToFirst()) { return cursor.getInt(0); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java index 64117b5060..0e42e5be5e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java @@ -62,6 +62,7 @@ import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.LifecycleCursorWrapper; import org.thoughtcrime.securesms.util.views.LearnMoreTextView; import org.thoughtcrime.securesms.util.views.SimpleProgressDialog; +import org.thoughtcrime.securesms.wallpaper.ChatWallpaperActivity; import java.util.List; import java.util.Locale; @@ -114,6 +115,7 @@ public class ManageGroupFragment extends LoggingFragment { private View toggleAllMembers; private View groupLinkRow; private TextView groupLinkButton; + private View wallpaperButton; private final Recipient.FallbackPhotoProvider fallbackPhotoProvider = new Recipient.FallbackPhotoProvider() { @Override @@ -175,6 +177,7 @@ public class ManageGroupFragment extends LoggingFragment { toggleAllMembers = view.findViewById(R.id.toggle_all_members); groupLinkRow = view.findViewById(R.id.group_link_row); groupLinkButton = view.findViewById(R.id.group_link_button); + wallpaperButton = view.findViewById(R.id.chat_wallpaper); return view; } @@ -240,6 +243,7 @@ public class ManageGroupFragment extends LoggingFragment { }); customNotificationsRow.setOnClickListener(v -> CustomNotificationsDialogFragment.create(groupRecipient.getId()) .show(requireFragmentManager(), DIALOG_TAG)); + wallpaperButton.setOnClickListener(v -> startActivity(ChatWallpaperActivity.createIntent(requireContext(), groupRecipient.getId()))); }); if (groupId.isV2()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/WallpaperValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/WallpaperValues.java index 2688d1e4fd..20764f7fe9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/WallpaperValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/WallpaperValues.java @@ -27,7 +27,6 @@ public final class WallpaperValues extends SignalStoreValues { @Override void onFirstEverAppLaunch() { - } public void setWallpaper(@NonNull Context context, @Nullable ChatWallpaper wallpaper) { @@ -44,7 +43,9 @@ public final class WallpaperValues extends SignalStoreValues { getStore().beginWrite().remove(KEY_WALLPAPER).apply(); } - WallpaperStorage.onWallpaperDeselected(context, currentUri); + if (currentUri != null) { + WallpaperStorage.onWallpaperDeselected(context, currentUri); + } } public @Nullable ChatWallpaper getWallpaper() { @@ -57,7 +58,30 @@ public final class WallpaperValues extends SignalStoreValues { } } - public @Nullable Uri getCurrentWallpaperUri() { + public boolean hasWallpaperSet() { + return getStore().getBlob(KEY_WALLPAPER, null) != null; + } + + public void setDimInDarkTheme(boolean enabled) { + Wallpaper currentWallpaper = getCurrentWallpaper(); + + if (currentWallpaper != null) { + putBlob(KEY_WALLPAPER, + currentWallpaper.toBuilder() + .setDimLevelInDarkTheme(enabled ? 0.2f : 0) + .build() + .toByteArray()); + } else { + throw new IllegalStateException("No wallpaper currently set!"); + } + } + + + /** + * Retrieves the URI of the current wallpaper. Note that this will only return a value if the + * wallpaper is both set *and* it's an image. + */ + public @Nullable Uri getWallpaperUri() { Wallpaper currentWallpaper = getCurrentWallpaper(); if (currentWallpaper != null && currentWallpaper.hasFile()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageHeaderViewHolder.java b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageHeaderViewHolder.java index 83e9dedce2..267403a776 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageHeaderViewHolder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageHeaderViewHolder.java @@ -88,7 +88,7 @@ final class MessageHeaderViewHolder extends RecyclerView.ViewHolder { conversationItem = (ConversationItem) receivedStub.inflate(); } } - conversationItem.bind(lifecycleOwner, conversationMessage, Optional.absent(), Optional.absent(), glideRequests, Locale.getDefault(), new HashSet<>(), conversationMessage.getMessageRecord().getRecipient(), null, false); + conversationItem.bind(lifecycleOwner, conversationMessage, Optional.absent(), Optional.absent(), glideRequests, Locale.getDefault(), new HashSet<>(), conversationMessage.getMessageRecord().getRecipient(), null, false, false); } private void bindErrorState(MessageRecord messageRecord) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java index 8e48b91ef0..5200b14e42 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java @@ -8,6 +8,7 @@ import androidx.preference.ListPreference; import org.thoughtcrime.securesms.ApplicationPreferencesActivity; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.util.ActivityTransitionUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.wallpaper.ChatWallpaperActivity; @@ -24,7 +25,8 @@ public class AppearancePreferenceFragment extends ListSummaryPreferenceFragment this.findPreference(TextSecurePreferences.THEME_PREF).setOnPreferenceChangeListener(new ListSummaryListener()); this.findPreference(TextSecurePreferences.LANGUAGE_PREF).setOnPreferenceChangeListener(new ListSummaryListener()); this.findPreference(WALLPAPER_PREF).setOnPreferenceClickListener(preference -> { - startActivity(ChatWallpaperActivity.getIntent(requireContext())); + startActivity(ChatWallpaperActivity.createIntent(requireContext())); + ActivityTransitionUtil.setSlideInTransition(requireActivity()); return true; }); initializeListSummary((ListPreference)findPreference(TextSecurePreferences.THEME_PREF)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java index 1604829ade..491eb52a8c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java @@ -848,7 +848,18 @@ public class Recipient { } public @Nullable ChatWallpaper getWallpaper() { - return wallpaper; + if (wallpaper != null) { + return wallpaper; + } else { + return SignalStore.wallpaper().getWallpaper(); + } + } + + /** + * A cheap way to check if wallpaper is set without doing any unnecessary proto parsing. + */ + public boolean hasWallpaper() { + return wallpaper != null || SignalStore.wallpaper().hasWallpaperSet(); } public boolean isSystemContact() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java index 44e1f05e1f..001f219f09 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java @@ -54,6 +54,7 @@ import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.LifecycleCursorWrapper; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.wallpaper.ChatWallpaperActivity; import java.util.Locale; import java.util.Objects; @@ -105,6 +106,7 @@ public class ManageRecipientFragment extends LoggingFragment { private View secureCallButton; private View insecureCallButton; private View secureVideoCallButton; + private View chatWallpaperButton; static ManageRecipientFragment newInstance(@NonNull RecipientId recipientId, boolean fromConversation) { ManageRecipientFragment fragment = new ManageRecipientFragment(); @@ -161,6 +163,7 @@ public class ManageRecipientFragment extends LoggingFragment { secureCallButton = view.findViewById(R.id.recipient_voice_call); insecureCallButton = view.findViewById(R.id.recipient_insecure_voice_call); secureVideoCallButton = view.findViewById(R.id.recipient_video_call); + chatWallpaperButton = view.findViewById(R.id.chat_wallpaper); return view; } @@ -270,6 +273,7 @@ public class ManageRecipientFragment extends LoggingFragment { secureCallButton.setOnClickListener(v -> viewModel.onSecureCall(requireActivity())); insecureCallButton.setOnClickListener(v -> viewModel.onInsecureCall(requireActivity())); secureVideoCallButton.setOnClickListener(v -> viewModel.onSecureVideoCall(requireActivity())); + chatWallpaperButton.setOnClickListener(v -> startActivity(ChatWallpaperActivity.createIntent(requireContext(), recipientId))); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ActivityTransitionUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ActivityTransitionUtil.java new file mode 100644 index 0000000000..60027f7aaa --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ActivityTransitionUtil.java @@ -0,0 +1,26 @@ +package org.thoughtcrime.securesms.util; + +import androidx.activity.ComponentActivity; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.R; + +public final class ActivityTransitionUtil { + + private ActivityTransitionUtil() {} + + /** + * To be used with finish + */ + public static void setSlideOutTransition(@NonNull ComponentActivity activity) { + activity.overridePendingTransition(R.anim.slide_from_start, R.anim.slide_to_end); + } + + /** + * To be used with startActivity + */ + public static void setSlideInTransition(@NonNull ComponentActivity activity) { + activity.overridePendingTransition(R.anim.slide_from_end, R.anim.slide_to_start); + } + +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaper.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaper.java index 62a43e1320..914d4dd783 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaper.java @@ -12,20 +12,31 @@ import java.util.List; public interface ChatWallpaper extends Parcelable { - List BUILTINS = Arrays.asList(GradientChatWallpaper.SOLID_1, - GradientChatWallpaper.SOLID_2, - GradientChatWallpaper.SOLID_3, - GradientChatWallpaper.SOLID_4, - GradientChatWallpaper.SOLID_5, - GradientChatWallpaper.SOLID_6, - GradientChatWallpaper.SOLID_7, - GradientChatWallpaper.SOLID_8, - GradientChatWallpaper.SOLID_9, - GradientChatWallpaper.SOLID_10, - GradientChatWallpaper.SOLID_11, - GradientChatWallpaper.SOLID_12, + float FIXED_DIM_LEVEL_FOR_DARK_THEME = 0.2f; + + List BUILTINS = Arrays.asList(SingleColorChatWallpaper.SOLID_1, + SingleColorChatWallpaper.SOLID_2, + SingleColorChatWallpaper.SOLID_3, + SingleColorChatWallpaper.SOLID_4, + SingleColorChatWallpaper.SOLID_5, + SingleColorChatWallpaper.SOLID_6, + SingleColorChatWallpaper.SOLID_7, + SingleColorChatWallpaper.SOLID_8, + SingleColorChatWallpaper.SOLID_9, + SingleColorChatWallpaper.SOLID_10, + SingleColorChatWallpaper.SOLID_11, + SingleColorChatWallpaper.SOLID_12, GradientChatWallpaper.GRADIENT_1, - GradientChatWallpaper.GRADIENT_2); + GradientChatWallpaper.GRADIENT_2, + GradientChatWallpaper.GRADIENT_3, + GradientChatWallpaper.GRADIENT_4, + GradientChatWallpaper.GRADIENT_5, + GradientChatWallpaper.GRADIENT_6, + GradientChatWallpaper.GRADIENT_7, + GradientChatWallpaper.GRADIENT_8, + GradientChatWallpaper.GRADIENT_9); + + float getDimLevelForDarkTheme(); void loadInto(@NonNull ImageView imageView); diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperActivity.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperActivity.java index c10cb73f33..b32bfe5777 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperActivity.java @@ -14,6 +14,7 @@ import androidx.navigation.Navigation; import org.thoughtcrime.securesms.PassphraseRequiredActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.recipients.RecipientId; +import org.thoughtcrime.securesms.util.ActivityTransitionUtil; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicTheme; @@ -23,11 +24,11 @@ public final class ChatWallpaperActivity extends PassphraseRequiredActivity { private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme(); - public static @NonNull Intent getIntent(@NonNull Context context) { - return getIntent(context, null); + public static @NonNull Intent createIntent(@NonNull Context context) { + return createIntent(context, null); } - public static @NonNull Intent getIntent(@NonNull Context context, @Nullable RecipientId recipientId) { + public static @NonNull Intent createIntent(@NonNull Context context, @Nullable RecipientId recipientId) { Intent intent = new Intent(context, ChatWallpaperActivity.class); intent.putExtra(EXTRA_RECIPIENT_ID, recipientId); return intent; @@ -35,8 +36,7 @@ public final class ChatWallpaperActivity extends PassphraseRequiredActivity { @Override protected void onCreate(Bundle savedInstanceState, boolean ready) { - ChatWallpaperViewModel.Factory factory = new ChatWallpaperViewModel.Factory(getIntent(this).getParcelableExtra(EXTRA_RECIPIENT_ID)); - + ChatWallpaperViewModel.Factory factory = new ChatWallpaperViewModel.Factory(getIntent().getParcelableExtra(EXTRA_RECIPIENT_ID)); ViewModelProviders.of(this, factory).get(ChatWallpaperViewModel.class); dynamicTheme.onCreate(this); @@ -47,6 +47,7 @@ public final class ChatWallpaperActivity extends PassphraseRequiredActivity { toolbar.setNavigationOnClickListener(unused -> { if (!Navigation.findNavController(this, R.id.nav_host_fragment).popBackStack()) { finish(); + ActivityTransitionUtil.setSlideOutTransition(this); } }); @@ -58,6 +59,12 @@ public final class ChatWallpaperActivity extends PassphraseRequiredActivity { } } + @Override + public void onBackPressed() { + super.onBackPressed(); + ActivityTransitionUtil.setSlideOutTransition(this); + } + @Override protected void onResume() { super.onResume(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperDimLevelUtil.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperDimLevelUtil.java new file mode 100644 index 0000000000..7c10536b46 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperDimLevelUtil.java @@ -0,0 +1,22 @@ +package org.thoughtcrime.securesms.wallpaper; + +import android.view.View; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.util.ThemeUtil; + +public final class ChatWallpaperDimLevelUtil { + + private ChatWallpaperDimLevelUtil() { + } + + public static void applyDimLevelForNightMode(@NonNull View dimmer, @NonNull ChatWallpaper chatWallpaper) { + if (ThemeUtil.isDarkTheme(dimmer.getContext())) { + dimmer.setAlpha(chatWallpaper.getDimLevelForDarkTheme()); + dimmer.setVisibility(View.VISIBLE); + } else { + dimmer.setVisibility(View.GONE); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperFactory.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperFactory.java index 90556237b8..782a09a6ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperFactory.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperFactory.java @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.wallpaper; import android.net.Uri; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper; @@ -9,25 +10,37 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper; /** * Converts persisted models of wallpaper into usable {@link ChatWallpaper} instances. */ -public class ChatWallpaperFactory { +public final class ChatWallpaperFactory { + + private ChatWallpaperFactory() {} public static @NonNull ChatWallpaper create(@NonNull Wallpaper model) { if (model.hasSingleColor()) { - return new GradientChatWallpaper(model.getSingleColor().getColor()); + return buildForSingleColor(model.getSingleColor(), model.getDimLevelInDarkTheme()); } else if (model.hasLinearGradient()) { - return buildForLinearGradinent(model.getLinearGradient()); + return buildForLinearGradinent(model.getLinearGradient(), model.getDimLevelInDarkTheme()); } else if (model.hasFile()) { - return buildForFile(model.getFile()); + return buildForFile(model.getFile(), model.getDimLevelInDarkTheme()); } else { throw new IllegalArgumentException(); } } - public static @NonNull ChatWallpaper create(@NonNull Uri uri) { - return new UriChatWallpaper(uri); + public static @NonNull ChatWallpaper updateWithDimming(@NonNull ChatWallpaper wallpaper, float dimLevelInDarkTheme) { + Wallpaper model = wallpaper.serialize(); + + return create(model.toBuilder().setDimLevelInDarkTheme(dimLevelInDarkTheme).build()); } - private static @NonNull ChatWallpaper buildForLinearGradinent(@NonNull Wallpaper.LinearGradient gradient) { + public static @NonNull ChatWallpaper create(@NonNull Uri uri) { + return new UriChatWallpaper(uri, 0f); + } + + private static @NonNull ChatWallpaper buildForSingleColor(@NonNull Wallpaper.SingleColor singleColor, float dimLevelInDarkTheme) { + return new SingleColorChatWallpaper(singleColor.getColor(), dimLevelInDarkTheme); + } + + private static @NonNull ChatWallpaper buildForLinearGradinent(@NonNull Wallpaper.LinearGradient gradient, float dimLevelInDarkTheme) { int[] colors = new int[gradient.getColorsCount()]; for (int i = 0; i < colors.length; i++) { colors[i] = gradient.getColors(i); @@ -38,11 +51,11 @@ public class ChatWallpaperFactory { positions[i] = gradient.getPositions(i); } - return new GradientChatWallpaper(gradient.getRotation(), colors, positions); + return new GradientChatWallpaper(gradient.getRotation(), colors, positions, dimLevelInDarkTheme); } - private static @NonNull ChatWallpaper buildForFile(@NonNull Wallpaper.File file) { + private static @NonNull ChatWallpaper buildForFile(@NonNull Wallpaper.File file, float dimLevelInDarkTheme) { Uri uri = Uri.parse(file.getUri()); - return new UriChatWallpaper(uri); + return new UriChatWallpaper(uri, dimLevelInDarkTheme); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperFragment.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperFragment.java index cb5f40661e..508552753f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperFragment.java @@ -8,12 +8,14 @@ import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.widget.SwitchCompat; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProviders; import androidx.navigation.Navigation; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.util.ThemeUtil; public class ChatWallpaperFragment extends Fragment { @Override @@ -26,18 +28,48 @@ public class ChatWallpaperFragment extends Fragment { ChatWallpaperViewModel viewModel = ViewModelProviders.of(requireActivity()).get(ChatWallpaperViewModel.class); ImageView chatWallpaperPreview = view.findViewById(R.id.chat_wallpaper_preview_background); View setWallpaper = view.findViewById(R.id.chat_wallpaper_set_wallpaper); - - viewModel.setWallpaper(GradientChatWallpaper.GRADIENT_1); + SwitchCompat dimInNightMode = view.findViewById(R.id.chat_wallpaper_dark_theme_dims_wallpaper); + View chatWallpaperDim = view.findViewById(R.id.chat_wallpaper_dim); + View clearWallpaper = view.findViewById(R.id.chat_wallpaper_clear_wallpaper); + View resetAllWallpaper = view.findViewById(R.id.chat_wallpaper_reset_all_wallpapers); viewModel.getCurrentWallpaper().observe(getViewLifecycleOwner(), wallpaper -> { if (wallpaper.isPresent()) { wallpaper.get().loadInto(chatWallpaperPreview); } else { + chatWallpaperPreview.setImageDrawable(null); chatWallpaperPreview.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.signal_background_primary)); } + + dimInNightMode.setEnabled(wallpaper.isPresent()); + }); + + viewModel.getDimInDarkTheme().observe(getViewLifecycleOwner(), shouldDimInNightMode -> { + if (shouldDimInNightMode != dimInNightMode.isChecked()) { + dimInNightMode.setChecked(shouldDimInNightMode); + } + + chatWallpaperDim.setAlpha(ChatWallpaper.FIXED_DIM_LEVEL_FOR_DARK_THEME); + chatWallpaperDim.setVisibility(shouldDimInNightMode && ThemeUtil.isDarkTheme(requireContext()) ? View.VISIBLE : View.GONE); }); setWallpaper.setOnClickListener(unused -> Navigation.findNavController(view) .navigate(R.id.action_chatWallpaperFragment_to_chatWallpaperSelectionFragment)); + + clearWallpaper.setOnClickListener(unused -> { + viewModel.setWallpaper(null); + viewModel.setDimInDarkTheme(false); + viewModel.saveWallpaperSelection(); + }); + + resetAllWallpaper.setVisibility(viewModel.isGlobal() ? View.VISIBLE : View.GONE); + + resetAllWallpaper.setOnClickListener(unused -> { + viewModel.setWallpaper(null); + viewModel.setDimInDarkTheme(false); + viewModel.resetAllWallpaper(); + }); + + dimInNightMode.setOnCheckedChangeListener((buttonView, isChecked) -> viewModel.setDimInDarkTheme(isChecked)); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperPreviewActivity.java index 249dc15cba..f3ed8fe429 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperPreviewActivity.java @@ -8,30 +8,33 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; -import androidx.lifecycle.ViewModelProviders; import androidx.viewpager2.widget.ViewPager2; +import com.annimon.stream.Stream; + import org.thoughtcrime.securesms.PassphraseRequiredActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.recipients.RecipientId; +import org.thoughtcrime.securesms.util.ActivityTransitionUtil; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.FullscreenHelper; +import org.thoughtcrime.securesms.util.MappingModel; import java.util.Collections; public class ChatWallpaperPreviewActivity extends PassphraseRequiredActivity { - private static final String EXTRA_CHAT_WALLPAPER = "extra.chat.wallpaper"; - private static final String EXTRA_RECIPIENT_ID = "extra.recipient.id"; + public static final String EXTRA_CHAT_WALLPAPER = "extra.chat.wallpaper"; + private static final String EXTRA_DIM_IN_DARK_MODE = "extra.dim.in.dark.mode"; private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme(); - public static @NonNull Intent create(@NonNull Context context, @NonNull ChatWallpaper selection, @Nullable RecipientId recipientId) { + public static @NonNull Intent create(@NonNull Context context, @NonNull ChatWallpaper selection, boolean dimInDarkMode) { Intent intent = new Intent(context, ChatWallpaperPreviewActivity.class); intent.putExtra(EXTRA_CHAT_WALLPAPER, selection); - intent.putExtra(EXTRA_RECIPIENT_ID, recipientId); + intent.putExtra(EXTRA_DIM_IN_DARK_MODE, dimInDarkMode); return intent; } @@ -42,32 +45,43 @@ public class ChatWallpaperPreviewActivity extends PassphraseRequiredActivity { setContentView(R.layout.chat_wallpaper_preview_activity); - ViewPager2 viewPager = findViewById(R.id.preview_pager); - ChatWallpaperPreviewAdapter adapter = new ChatWallpaperPreviewAdapter(); - View submit = findViewById(R.id.preview_set_wallpaper); - ChatWallpaperViewModel.Factory factory = new ChatWallpaperViewModel.Factory(getIntent().getParcelableExtra(EXTRA_RECIPIENT_ID)); - ChatWallpaperViewModel viewModel = ViewModelProviders.of(this, factory).get(ChatWallpaperViewModel.class); - ChatWallpaper selected = getIntent().getParcelableExtra(EXTRA_CHAT_WALLPAPER); - Toolbar toolbar = findViewById(R.id.toolbar); + ViewPager2 viewPager = findViewById(R.id.preview_pager); + ChatWallpaperPreviewAdapter adapter = new ChatWallpaperPreviewAdapter(); + View submit = findViewById(R.id.preview_set_wallpaper); + ChatWallpaperRepository repository = new ChatWallpaperRepository(); + ChatWallpaper selected = getIntent().getParcelableExtra(EXTRA_CHAT_WALLPAPER); + boolean dim = getIntent().getBooleanExtra(EXTRA_DIM_IN_DARK_MODE, false); + Toolbar toolbar = findViewById(R.id.toolbar); - toolbar.setNavigationOnClickListener(unused -> finish()); + toolbar.setNavigationOnClickListener(unused -> { + finish(); + ActivityTransitionUtil.setSlideOutTransition(this); + }); viewPager.setAdapter(adapter); adapter.submitList(Collections.singletonList(new ChatWallpaperSelectionMappingModel(selected))); - viewModel.getWallpapers().observe(this, adapter::submitList); + repository.getAllWallpaper(wallpapers -> adapter.submitList(Stream.of(wallpapers) + .map(wallpaper -> ChatWallpaperFactory.updateWithDimming(wallpaper, dim ? 1f : 0f)) + .>map(ChatWallpaperSelectionMappingModel::new) + .toList())); submit.setOnClickListener(unused -> { ChatWallpaperSelectionMappingModel model = (ChatWallpaperSelectionMappingModel) adapter.getCurrentList().get(viewPager.getCurrentItem()); - viewModel.saveWallpaperSelection(model.getWallpaper()); - setResult(RESULT_OK); + setResult(RESULT_OK, new Intent().putExtra(EXTRA_CHAT_WALLPAPER, model.getWallpaper())); finish(); }); new FullscreenHelper(this).showSystemUI(); } + @Override + public void onBackPressed() { + super.onBackPressed(); + ActivityTransitionUtil.setSlideOutTransition(this); + } + @Override protected void onResume() { super.onResume(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperRepository.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperRepository.java index e95ce8b027..d9ce5f738a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperRepository.java @@ -1,14 +1,64 @@ package org.thoughtcrime.securesms.wallpaper; +import androidx.annotation.MainThread; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.util.Consumer; +import org.signal.core.util.concurrent.SignalExecutors; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientId; +import org.thoughtcrime.securesms.util.concurrent.SerialExecutor; +import org.whispersystems.libsignal.util.guava.Optional; + import java.util.List; +import java.util.concurrent.Executor; class ChatWallpaperRepository { + private static final Executor EXECUTOR = new SerialExecutor(SignalExecutors.BOUNDED); + + @MainThread + @Nullable ChatWallpaper getCurrentWallpaper(@Nullable RecipientId recipientId) { + if (recipientId != null) { + return Recipient.resolved(recipientId).getWallpaper(); + } else { + return SignalStore.wallpaper().getWallpaper(); + } + } + void getAllWallpaper(@NonNull Consumer> consumer) { consumer.accept(ChatWallpaper.BUILTINS); } + void saveWallpaper(@Nullable RecipientId recipientId, @Nullable ChatWallpaper chatWallpaper) { + if (recipientId != null) { + //noinspection CodeBlock2Expr + EXECUTOR.execute(() -> { + DatabaseFactory.getRecipientDatabase(ApplicationDependencies.getApplication()).setWallpaper(recipientId, chatWallpaper); + }); + } else { + SignalStore.wallpaper().setWallpaper(ApplicationDependencies.getApplication(), chatWallpaper); + } + } + + void resetAllWallpaper() { + SignalStore.wallpaper().setWallpaper(ApplicationDependencies.getApplication(), null); + EXECUTOR.execute(() -> { + DatabaseFactory.getRecipientDatabase(ApplicationDependencies.getApplication()).resetAllWallpaper(); + }); + } + + void setDimInDarkTheme(@NonNull RecipientId recipientId, boolean dimInDarkTheme) { + if (recipientId != null) { + EXECUTOR.execute(() -> { + DatabaseFactory.getRecipientDatabase(ApplicationDependencies.getApplication()).setDimWallpaperInDarkTheme(recipientId, dimInDarkTheme); + }); + } else { + SignalStore.wallpaper().setDimInDarkTheme(dimInDarkTheme); + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperSelectionFragment.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperSelectionFragment.java index 9e04ecd4c2..30b91d885e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperSelectionFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperSelectionFragment.java @@ -19,6 +19,7 @@ import com.google.android.flexbox.FlexboxLayoutManager; import com.google.android.flexbox.JustifyContent; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.util.ActivityTransitionUtil; public class ChatWallpaperSelectionFragment extends Fragment { @@ -45,7 +46,8 @@ public class ChatWallpaperSelectionFragment extends Fragment { @SuppressWarnings("CodeBlock2Expr") ChatWallpaperSelectionAdapter adapter = new ChatWallpaperSelectionAdapter(chatWallpaper -> { - startActivityForResult(ChatWallpaperPreviewActivity.create(requireActivity(), chatWallpaper, viewModel.getRecipientId()), PREVIEW); + startActivityForResult(ChatWallpaperPreviewActivity.create(requireActivity(), chatWallpaper, viewModel.getDimInDarkTheme().getValue()), PREVIEW); + ActivityTransitionUtil.setSlideInTransition(requireActivity()); }); flexboxLayoutManager.setJustifyContent(JustifyContent.SPACE_AROUND); @@ -63,9 +65,15 @@ public class ChatWallpaperSelectionFragment extends Fragment { if (uri == null || uri == Uri.EMPTY) { throw new AssertionError("Should never have an empty uri."); } else { - startActivityForResult(ChatWallpaperPreviewActivity.create(requireActivity(), new UriChatWallpaper(uri), viewModel.getRecipientId()), PREVIEW); + ChatWallpaper wallpaper = ChatWallpaperFactory.create(uri); + viewModel.setWallpaper(wallpaper); + viewModel.saveWallpaperSelection(); + Navigation.findNavController(requireView()).popBackStack(); } - } else if (requestCode == PREVIEW && resultCode == Activity.RESULT_OK) { + } else if (requestCode == PREVIEW && resultCode == Activity.RESULT_OK && data != null) { + ChatWallpaper chatWallpaper = data.getParcelableExtra(ChatWallpaperPreviewActivity.EXTRA_CHAT_WALLPAPER); + viewModel.setWallpaper(chatWallpaper); + viewModel.saveWallpaperSelection(); Navigation.findNavController(requireView()).popBackStack(); } else { super.onActivityResult(requestCode, resultCode, data); diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperViewHolder.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperViewHolder.java index 120e6e6974..3e273a779a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperViewHolder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperViewHolder.java @@ -11,15 +11,18 @@ import androidx.recyclerview.widget.RecyclerView; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.util.MappingAdapter; import org.thoughtcrime.securesms.util.MappingViewHolder; +import org.thoughtcrime.securesms.util.ThemeUtil; class ChatWallpaperViewHolder extends MappingViewHolder { private final ImageView preview; + private final View dimmer; private final EventListener eventListener; public ChatWallpaperViewHolder(@NonNull View itemView, @Nullable EventListener eventListener) { super(itemView); this.preview = itemView.findViewById(R.id.chat_wallpaper_preview); + this.dimmer = itemView.findViewById(R.id.chat_wallpaper_dim); this.eventListener = eventListener; } @@ -27,6 +30,8 @@ class ChatWallpaperViewHolder extends MappingViewHolder { if (getAdapterPosition() != RecyclerView.NO_POSITION) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperViewModel.java index 8acbc4dc96..91d6d036fe 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/ChatWallpaperViewModel.java @@ -4,7 +4,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.Transformations; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; @@ -12,6 +11,7 @@ import com.annimon.stream.Stream; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.MappingModel; +import org.thoughtcrime.securesms.util.livedata.LiveDataUtil; import org.whispersystems.libsignal.util.guava.Optional; import java.util.List; @@ -19,23 +19,52 @@ import java.util.Objects; public class ChatWallpaperViewModel extends ViewModel { - private final ChatWallpaperRepository repository = new ChatWallpaperRepository(); - private final MutableLiveData> wallpaper = new MutableLiveData<>(); - private final MutableLiveData> builtins = new MutableLiveData<>(); + private final ChatWallpaperRepository repository = new ChatWallpaperRepository(); + private final MutableLiveData> wallpaper = new MutableLiveData<>(); + private final MutableLiveData> builtins = new MutableLiveData<>(); + private final MutableLiveData dimInDarkTheme = new MutableLiveData<>(); private final RecipientId recipientId; private ChatWallpaperViewModel(@Nullable RecipientId recipientId) { this.recipientId = recipientId; + ChatWallpaper currentWallpaper = repository.getCurrentWallpaper(recipientId); + dimInDarkTheme.setValue(currentWallpaper != null && currentWallpaper.getDimLevelForDarkTheme() > 0f); + wallpaper.setValue(Optional.fromNullable(currentWallpaper)); repository.getAllWallpaper(builtins::postValue); } + void setDimInDarkTheme(boolean shouldDimInDarkTheme) { + dimInDarkTheme.setValue(shouldDimInDarkTheme); + + Optional wallpaper = this.wallpaper.getValue(); + if (wallpaper.isPresent()) { + repository.setDimInDarkTheme(recipientId, shouldDimInDarkTheme); + } + } + void setWallpaper(@Nullable ChatWallpaper chatWallpaper) { wallpaper.setValue(Optional.fromNullable(chatWallpaper)); } - void saveWallpaperSelection(@NonNull ChatWallpaper selected) { - // TODO + void saveWallpaperSelection() { + Optional wallpaper = this.wallpaper.getValue(); + boolean dimInDarkTheme = this.dimInDarkTheme.getValue(); + + if (!wallpaper.isPresent()) { + repository.saveWallpaper(recipientId, null); + return; + } + + Optional updated = wallpaper.transform(paper -> ChatWallpaperFactory.updateWithDimming(paper, dimInDarkTheme ? ChatWallpaper.FIXED_DIM_LEVEL_FOR_DARK_THEME : 0f)); + + if (updated.isPresent()) { + repository.saveWallpaper(recipientId, updated.get()); + } + } + + void resetAllWallpaper() { + repository.resetAllWallpaper(); } public @Nullable RecipientId getRecipientId() { @@ -47,8 +76,19 @@ public class ChatWallpaperViewModel extends ViewModel { } LiveData>> getWallpapers() { - return Transformations.map(Transformations.distinctUntilChanged(builtins), - wallpapers -> Stream.of(wallpapers).>map(ChatWallpaperSelectionMappingModel::new).toList()); + return LiveDataUtil.combineLatest(builtins, dimInDarkTheme, (wallpapers, dimInDarkMode) -> + Stream.of(wallpapers) + .map(paper -> ChatWallpaperFactory.updateWithDimming(paper, dimInDarkMode ? 1f : 0f)) + .>map(ChatWallpaperSelectionMappingModel::new).toList() + ); + } + + LiveData getDimInDarkTheme() { + return dimInDarkTheme; + } + + boolean isGlobal() { + return recipientId == null; } public static class Factory implements ViewModelProvider.Factory { diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/GradientChatWallpaper.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/GradientChatWallpaper.java index 6fc7b91711..950bfea9a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/GradientChatWallpaper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/GradientChatWallpaper.java @@ -23,43 +23,61 @@ import java.util.Objects; final class GradientChatWallpaper implements ChatWallpaper, Parcelable { - public static final GradientChatWallpaper SOLID_1 = new GradientChatWallpaper(0xFFE26983); - public static final GradientChatWallpaper SOLID_2 = new GradientChatWallpaper(0xFFDF9171); - public static final GradientChatWallpaper SOLID_3 = new GradientChatWallpaper(0xFF9E9887); - public static final GradientChatWallpaper SOLID_4 = new GradientChatWallpaper(0xFF89AE8F); - public static final GradientChatWallpaper SOLID_5 = new GradientChatWallpaper(0xFF32C7E2); - public static final GradientChatWallpaper SOLID_6 = new GradientChatWallpaper(0xFF7C99B6); - public static final GradientChatWallpaper SOLID_7 = new GradientChatWallpaper(0xFFC988E7); - public static final GradientChatWallpaper SOLID_8 = new GradientChatWallpaper(0xFFE297C3); - public static final GradientChatWallpaper SOLID_9 = new GradientChatWallpaper(0xFFA2A2AA); - public static final GradientChatWallpaper SOLID_10 = new GradientChatWallpaper(0xFF146148); - public static final GradientChatWallpaper SOLID_11 = new GradientChatWallpaper(0xFF403B91); - public static final GradientChatWallpaper SOLID_12 = new GradientChatWallpaper(0xFF624249); - public static final GradientChatWallpaper GRADIENT_1 = new GradientChatWallpaper(167.96f, - new int[] { 0xFFF3DC47, 0xFFF3DA47, 0xFFF2D546, 0xFFF2CC46, 0xFFF1C146, 0xFFEFB445, 0xFFEEA544, 0xFFEC9644, 0xFFEB8743, 0xFFE97743, 0xFFE86942, 0xFFE65C41, 0xFFE55041, 0xFFE54841, 0xFFE44240, 0xFFE44040 }, - new float[] { 0.0f, 0.0807f, 0.1554f, 0.225f, 0.2904f, 0.3526f, 0.4125f, 0.471f, 0.529f, 0.5875f, 0.6474f, 0.7096f, 0.775f, 0.8446f, 0.9193f, 1f }); - public static final GradientChatWallpaper GRADIENT_2 = new GradientChatWallpaper(180f, - new int[] { 0xFF16161D, 0xFF17171E, 0xFF1A1A22, 0xFF1F1F28, 0xFF26262F, 0xFF2D2D38, 0xFF353542, 0xFF3E3E4C, 0xFF474757, 0xFF4F4F61, 0xFF57576B, 0xFF5F5F74, 0xFF65657C, 0xFF6A6A82, 0xFF6D6D85, 0xFF6E6E87 }, - new float[] { 0.0000f, 0.0807f, 0.1554f, 0.2250f, 0.2904f, 0.3526f, 0.4125f, 0.4710f, 0.5290f, 0.5875f, 0.6474f, 0.7096f, 0.7750f, 0.8446f, 0.9193f, 1.0000f }); + public static final ChatWallpaper GRADIENT_1 = new GradientChatWallpaper(167.96f, + new int[] { 0xFFF3DC47, 0xFFF3DA47, 0xFFF2D546, 0xFFF2CC46, 0xFFF1C146, 0xFFEFB445, 0xFFEEA544, 0xFFEC9644, 0xFFEB8743, 0xFFE97743, 0xFFE86942, 0xFFE65C41, 0xFFE55041, 0xFFE54841, 0xFFE44240, 0xFFE44040 }, + new float[] { 0.0f, 0.0807f, 0.1554f, 0.225f, 0.2904f, 0.3526f, 0.4125f, 0.471f, 0.529f, 0.5875f, 0.6474f, 0.7096f, 0.775f, 0.8446f, 0.9193f, 1f }, + 0f); + public static final ChatWallpaper GRADIENT_2 = new GradientChatWallpaper(180f, + new int[] { 0xFF16161D, 0xFF17171E, 0xFF1A1A22, 0xFF1F1F28, 0xFF26262F, 0xFF2D2D38, 0xFF353542, 0xFF3E3E4C, 0xFF474757, 0xFF4F4F61, 0xFF57576B, 0xFF5F5F74, 0xFF65657C, 0xFF6A6A82, 0xFF6D6D85, 0xFF6E6E87 }, + new float[] { 0.0000f, 0.0807f, 0.1554f, 0.2250f, 0.2904f, 0.3526f, 0.4125f, 0.4710f, 0.5290f, 0.5875f, 0.6474f, 0.7096f, 0.7750f, 0.8446f, 0.9193f, 1.0000f }, + 0f); + public static final ChatWallpaper GRADIENT_3 = new GradientChatWallpaper(192.04f, + new int[] { 0xFFF53844, 0xFFF33845, 0xFFEC3848, 0xFFE2384C, 0xFFD63851, 0xFFC73857, 0xFFB6385E, 0xFFA43866, 0xFF93376D, 0xFF813775, 0xFF70377C, 0xFF613782, 0xFF553787, 0xFF4B378B, 0xFF44378E, 0xFF42378F }, + new float[] { 0.0000f, 0.0075f, 0.0292f, 0.0637f, 0.1097f, 0.1659f, 0.2310f, 0.3037f, 0.3827f, 0.4666f, 0.5541f, 0.6439f, 0.7347f, 0.8252f, 0.9141f, 1.0000f }, + 0f); + public static final ChatWallpaper GRADIENT_4 = new GradientChatWallpaper(180f, + new int[] { 0xFF0093E9, 0xFF0294E9, 0xFF0696E7, 0xFF0D99E5, 0xFF169EE3, 0xFF21A3E0, 0xFF2DA8DD, 0xFF3AAEDA, 0xFF46B5D6, 0xFF53BBD3, 0xFF5FC0D0, 0xFF6AC5CD, 0xFF73CACB, 0xFF7ACDC9, 0xFF7ECFC7, 0xFF80D0C7 }, + new float[] { 0.0000f, 0.0807f, 0.1554f, 0.2250f, 0.2904f, 0.3526f, 0.4125f, 0.4710f, 0.5290f, 0.5875f, 0.6474f, 0.7096f, 0.7750f, 0.8446f, 0.9193f, 1.0000f }, + 0f); + public static final ChatWallpaper GRADIENT_5 = new GradientChatWallpaper(192.04f, + new int[] { 0xFFF04CE6, 0xFFEE4BE6, 0xFFE54AE5, 0xFFD949E5, 0xFFC946E4, 0xFFB644E3, 0xFFA141E3, 0xFF8B3FE2, 0xFF743CE1, 0xFF5E39E0, 0xFF4936DF, 0xFF3634DE, 0xFF2632DD, 0xFF1930DD, 0xFF112FDD, 0xFF0E2FDD }, + new float[] { 0.0000f, 0.0807f, 0.1554f, 0.2250f, 0.2904f, 0.3526f, 0.4125f, 0.4710f, 0.5290f, 0.5875f, 0.6474f, 0.7096f, 0.7750f, 0.8446f, 0.9193f, 1.0000f }, + 0f); + public static final ChatWallpaper GRADIENT_6 = new GradientChatWallpaper(180f, + new int[] { 0xFF65CDAC, 0xFF64CDAB, 0xFF60CBA8, 0xFF5BC8A3, 0xFF55C49D, 0xFF4DC096, 0xFF45BB8F, 0xFF3CB687, 0xFF33B17F, 0xFF2AAC76, 0xFF21A76F, 0xFF1AA268, 0xFF139F62, 0xFF0E9C5E, 0xFF0B9A5B, 0xFF0A995A }, + new float[] { 0.0000f, 0.0807f, 0.1554f, 0.2250f, 0.2904f, 0.3526f, 0.4125f, 0.4710f, 0.5290f, 0.5875f, 0.6474f, 0.7096f, 0.7750f, 0.8446f, 0.9193f, 1.0000f }, + 0f); + public static final ChatWallpaper GRADIENT_7 = new GradientChatWallpaper(180f, + new int[] { 0xFFD8E1FA, 0xFFD8E0F9, 0xFFD8DEF7, 0xFFD8DBF3, 0xFFD8D6EE, 0xFFD7D1E8, 0xFFD7CCE2, 0xFFD7C6DB, 0xFFD7BFD4, 0xFFD7B9CD, 0xFFD6B4C7, 0xFFD6AFC1, 0xFFD6AABC, 0xFFD6A7B8, 0xFFD6A5B6, 0xFFD6A4B5 }, + new float[] { 0.0000f, 0.0807f, 0.1554f, 0.2250f, 0.2904f, 0.3526f, 0.4125f, 0.4710f, 0.5290f, 0.5875f, 0.6474f, 0.7096f, 0.7750f, 0.8446f, 0.9193f, 1.0000f }, + 0f); + public static final ChatWallpaper GRADIENT_8 = new GradientChatWallpaper(180f, + new int[] { 0xFFD8EBFD, 0xFFD7EAFD, 0xFFD5E9FD, 0xFFD2E7FD, 0xFFCDE5FD, 0xFFC8E3FD, 0xFFC3E0FD, 0xFFBDDDFC, 0xFFB7DAFC, 0xFFB2D7FC, 0xFFACD4FC, 0xFFA7D1FC, 0xFFA3CFFB, 0xFFA0CDFB, 0xFF9ECCFB, 0xFF9DCCFB }, + new float[] { 0.0000f, 0.0807f, 0.1554f, 0.2250f, 0.2904f, 0.3526f, 0.4125f, 0.4710f, 0.5290f, 0.5875f, 0.6474f, 0.7096f, 0.7750f, 0.8446f, 0.9193f, 1.0000f }, + 0f); + public static final ChatWallpaper GRADIENT_9 = new GradientChatWallpaper(192.04f, + new int[] { 0xFFFFE5C2, 0xFFFFE4C1, 0xFFFFE2BF, 0xFFFFDFBD, 0xFFFEDBB9, 0xFFFED6B5, 0xFFFED1B1, 0xFFFDCCAC, 0xFFFDC6A8, 0xFFFDC0A3, 0xFFFCBB9F, 0xFFFCB69B, 0xFFFCB297, 0xFFFCAF95, 0xFFFCAD93, 0xFFFCAC92 }, + new float[] { 0.0000f, 0.0807f, 0.1554f, 0.2250f, 0.2904f, 0.3526f, 0.4125f, 0.4710f, 0.5290f, 0.5875f, 0.6474f, 0.7096f, 0.7750f, 0.8446f, 0.9193f, 1.0000f }, + 0f); + private final float degrees; private final int[] colors; private final float[] positions; + private final float dimLevelInDarkTheme; - GradientChatWallpaper(int color) { - this(0f, new int[]{color, color}, null); - } - - GradientChatWallpaper(float degrees, int[] colors, float[] positions) { - this.degrees = degrees; - this.colors = colors; - this.positions = positions; + GradientChatWallpaper(float degrees, int[] colors, float[] positions, float dimLevelInDarkTheme) { + this.degrees = degrees; + this.colors = colors; + this.positions = positions; + this.dimLevelInDarkTheme = dimLevelInDarkTheme; } private GradientChatWallpaper(Parcel in) { - degrees = in.readFloat(); - colors = in.createIntArray(); - positions = in.createFloatArray(); + degrees = in.readFloat(); + colors = in.createIntArray(); + positions = in.createFloatArray(); + dimLevelInDarkTheme = in.readFloat(); } @Override @@ -67,6 +85,7 @@ final class GradientChatWallpaper implements ChatWallpaper, Parcelable { dest.writeFloat(degrees); dest.writeIntArray(colors); dest.writeFloatArray(positions); + dest.writeFloat(dimLevelInDarkTheme); } @Override @@ -78,6 +97,11 @@ final class GradientChatWallpaper implements ChatWallpaper, Parcelable { return new RotatableGradientDrawable(degrees, colors, positions); } + @Override + public float getDimLevelForDarkTheme() { + return dimLevelInDarkTheme; + } + @Override public void loadInto(@NonNull ImageView imageView) { imageView.setImageDrawable(buildDrawable()); @@ -99,6 +123,7 @@ final class GradientChatWallpaper implements ChatWallpaper, Parcelable { return Wallpaper.newBuilder() .setLinearGradient(builder) + .setDimLevelInDarkTheme(dimLevelInDarkTheme) .build(); } @@ -109,12 +134,13 @@ final class GradientChatWallpaper implements ChatWallpaper, Parcelable { GradientChatWallpaper that = (GradientChatWallpaper) o; return Float.compare(that.degrees, degrees) == 0 && Arrays.equals(colors, that.colors) && - Arrays.equals(positions, that.positions); + Arrays.equals(positions, that.positions) && + Float.compare(that.dimLevelInDarkTheme, dimLevelInDarkTheme) == 0; } @Override public int hashCode() { - int result = Objects.hash(degrees); + int result = Objects.hash(degrees, dimLevelInDarkTheme); result = 31 * result + Arrays.hashCode(colors); result = 31 * result + Arrays.hashCode(positions); return result; diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/SingleColorChatWallpaper.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/SingleColorChatWallpaper.java new file mode 100644 index 0000000000..f337754c62 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/SingleColorChatWallpaper.java @@ -0,0 +1,100 @@ +package org.thoughtcrime.securesms.wallpaper; + +import android.graphics.drawable.ColorDrawable; +import android.os.Parcel; +import android.os.Parcelable; +import android.widget.ImageView; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper; + +import java.util.Objects; + +final class SingleColorChatWallpaper implements ChatWallpaper, Parcelable { + + public static final ChatWallpaper SOLID_1 = new SingleColorChatWallpaper(0xFFE26983, 0f); + public static final ChatWallpaper SOLID_2 = new SingleColorChatWallpaper(0xFFDF9171, 0f); + public static final ChatWallpaper SOLID_3 = new SingleColorChatWallpaper(0xFF9E9887, 0f); + public static final ChatWallpaper SOLID_4 = new SingleColorChatWallpaper(0xFF89AE8F, 0f); + public static final ChatWallpaper SOLID_5 = new SingleColorChatWallpaper(0xFF32C7E2, 0f); + public static final ChatWallpaper SOLID_6 = new SingleColorChatWallpaper(0xFF7C99B6, 0f); + public static final ChatWallpaper SOLID_7 = new SingleColorChatWallpaper(0xFFC988E7, 0f); + public static final ChatWallpaper SOLID_8 = new SingleColorChatWallpaper(0xFFE297C3, 0f); + public static final ChatWallpaper SOLID_9 = new SingleColorChatWallpaper(0xFFA2A2AA, 0f); + public static final ChatWallpaper SOLID_10 = new SingleColorChatWallpaper(0xFF146148, 0f); + public static final ChatWallpaper SOLID_11 = new SingleColorChatWallpaper(0xFF403B91, 0f); + public static final ChatWallpaper SOLID_12 = new SingleColorChatWallpaper(0xFF624249, 0f); + + private final @ColorInt int color; + private final float dimLevelInDarkTheme; + + SingleColorChatWallpaper(@ColorInt int color, float dimLevelInDarkTheme) { + this.color = color; + this.dimLevelInDarkTheme = dimLevelInDarkTheme; + } + + private SingleColorChatWallpaper(Parcel in) { + color = in.readInt(); + dimLevelInDarkTheme = in.readFloat(); + } + + @Override + public float getDimLevelForDarkTheme() { + return dimLevelInDarkTheme; + } + + @Override + public void loadInto(@NonNull ImageView imageView) { + imageView.setImageDrawable(new ColorDrawable(color)); + } + + @Override + public @NonNull Wallpaper serialize() { + Wallpaper.SingleColor.Builder builder = Wallpaper.SingleColor.newBuilder(); + + builder.setColor(color); + + return Wallpaper.newBuilder() + .setSingleColor(builder) + .setDimLevelInDarkTheme(dimLevelInDarkTheme) + .build(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(color); + dest.writeFloat(dimLevelInDarkTheme); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SingleColorChatWallpaper that = (SingleColorChatWallpaper) o; + return color == that.color && Float.compare(dimLevelInDarkTheme, that.dimLevelInDarkTheme) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(color, dimLevelInDarkTheme); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SingleColorChatWallpaper createFromParcel(Parcel in) { + return new SingleColorChatWallpaper(in); + } + + @Override + public SingleColorChatWallpaper[] newArray(int size) { + return new SingleColorChatWallpaper[size]; + } + }; +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/UriChatWallpaper.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/UriChatWallpaper.java index f199018825..5b41df04f1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/UriChatWallpaper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/UriChatWallpaper.java @@ -10,12 +10,21 @@ import androidx.annotation.NonNull; import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper; import org.thoughtcrime.securesms.mms.GlideApp; +import java.util.Objects; + final class UriChatWallpaper implements ChatWallpaper, Parcelable { - private final Uri uri; + private final Uri uri; + private final float dimLevelInDarkTheme; - public UriChatWallpaper(@NonNull Uri uri) { - this.uri = uri; + public UriChatWallpaper(@NonNull Uri uri, float dimLevelInDarkTheme) { + this.uri = uri; + this.dimLevelInDarkTheme = dimLevelInDarkTheme; + } + + @Override + public float getDimLevelForDarkTheme() { + return dimLevelInDarkTheme; } @Override @@ -29,6 +38,7 @@ final class UriChatWallpaper implements ChatWallpaper, Parcelable { public @NonNull Wallpaper serialize() { return Wallpaper.newBuilder() .setFile(Wallpaper.File.newBuilder().setUri(uri.toString())) + .setDimLevelInDarkTheme(dimLevelInDarkTheme) .build(); } @@ -40,12 +50,27 @@ final class UriChatWallpaper implements ChatWallpaper, Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(uri.toString()); + dest.writeFloat(dimLevelInDarkTheme); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + UriChatWallpaper that = (UriChatWallpaper) o; + return Float.compare(that.dimLevelInDarkTheme, dimLevelInDarkTheme) == 0 && + uri.equals(that.uri); + } + + @Override + public int hashCode() { + return Objects.hash(uri, dimLevelInDarkTheme); } public static final Creator CREATOR = new Creator() { @Override public UriChatWallpaper createFromParcel(Parcel in) { - return new UriChatWallpaper(Uri.parse(in.readString())); + return new UriChatWallpaper(Uri.parse(in.readString()), in.readFloat()); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/WallpaperStorage.java b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/WallpaperStorage.java index eb3e32032d..cfe7dd4ee6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/wallpaper/WallpaperStorage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/wallpaper/WallpaperStorage.java @@ -74,7 +74,7 @@ public final class WallpaperStorage { */ @WorkerThread public static void onWallpaperDeselected(@NonNull Context context, @NonNull Uri uri) { - Uri globalUri = SignalStore.wallpaper().getCurrentWallpaperUri(); + Uri globalUri = SignalStore.wallpaper().getWallpaperUri(); if (Objects.equals(uri, globalUri)) { return; } diff --git a/app/src/main/proto/Database.proto b/app/src/main/proto/Database.proto index 11bc7fe7e8..bbf45539a7 100644 --- a/app/src/main/proto/Database.proto +++ b/app/src/main/proto/Database.proto @@ -96,11 +96,13 @@ message Wallpaper { message SingleColor { int32 color = 1; } + message LinearGradient { float rotation = 1; repeated int32 colors = 2; repeated float positions = 3; } + message File { string uri = 1; } @@ -111,5 +113,5 @@ message Wallpaper { File file = 3; } - float dimLevelInDarkMode = 4; + float dimLevelInDarkTheme = 4; } \ No newline at end of file diff --git a/app/src/main/res/drawable/chat_wallpaper_preview_bottom_bar.xml b/app/src/main/res/drawable/chat_wallpaper_preview_bottom_bar.xml index b449b34d7f..66b110a2b9 100644 --- a/app/src/main/res/drawable/chat_wallpaper_preview_bottom_bar.xml +++ b/app/src/main/res/drawable/chat_wallpaper_preview_bottom_bar.xml @@ -1,5 +1,14 @@ - - - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/chat_wallpaper_preview_bubble_10.xml b/app/src/main/res/drawable/chat_wallpaper_preview_bubble_10.xml new file mode 100644 index 0000000000..5400fcd02d --- /dev/null +++ b/app/src/main/res/drawable/chat_wallpaper_preview_bubble_10.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/chat_wallpaper_preview_bubble.xml b/app/src/main/res/drawable/chat_wallpaper_preview_bubble_8.xml similarity index 100% rename from app/src/main/res/drawable/chat_wallpaper_preview_bubble.xml rename to app/src/main/res/drawable/chat_wallpaper_preview_bubble_8.xml diff --git a/app/src/main/res/drawable/chat_wallpaper_preview_top_bar.xml b/app/src/main/res/drawable/chat_wallpaper_preview_top_bar.xml index c2d3fbffbc..eb4939c679 100644 --- a/app/src/main/res/drawable/chat_wallpaper_preview_top_bar.xml +++ b/app/src/main/res/drawable/chat_wallpaper_preview_top_bar.xml @@ -1,5 +1,14 @@ - - - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/wallpaper_bubble_background_11.xml b/app/src/main/res/drawable/wallpaper_bubble_background_11.xml new file mode 100644 index 0000000000..9951fdecf9 --- /dev/null +++ b/app/src/main/res/drawable/wallpaper_bubble_background_11.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/drawable/wallpaper_bubble_background_12.xml b/app/src/main/res/drawable/wallpaper_bubble_background_12.xml new file mode 100644 index 0000000000..c95f561dfa --- /dev/null +++ b/app/src/main/res/drawable/wallpaper_bubble_background_12.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/drawable/wallpaper_bubble_background_8.xml b/app/src/main/res/drawable/wallpaper_bubble_background_8.xml new file mode 100644 index 0000000000..2868bf819e --- /dev/null +++ b/app/src/main/res/drawable/wallpaper_bubble_background_8.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/drawable/wallpaper_bubble_background_tintable_11.xml b/app/src/main/res/drawable/wallpaper_bubble_background_tintable_11.xml new file mode 100644 index 0000000000..6bb1316b21 --- /dev/null +++ b/app/src/main/res/drawable/wallpaper_bubble_background_tintable_11.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/layout/chat_wallpaper_fragment.xml b/app/src/main/res/layout/chat_wallpaper_fragment.xml index 695839a65f..ecaceaeb79 100644 --- a/app/src/main/res/layout/chat_wallpaper_fragment.xml +++ b/app/src/main/res/layout/chat_wallpaper_fragment.xml @@ -15,44 +15,65 @@ android:layout_width="0dp" android:layout_height="320dp" android:background="@color/signal_background_tertiary" - app:layout_constraintBottom_toBottomOf="@id/chat_wallpaper_preview_bottom_bar" + app:layout_constraintBottom_toBottomOf="@id/chat_wallpaper_preview_background" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - + app:layout_constraintTop_toTopOf="@id/chat_wallpaper_preview_background" /> + + + tools:visibility="visible" /> + + + app:layout_constraintStart_toStartOf="parent" /> + + - - - + - \ No newline at end of file + android:layout_height="match_parent"> + + + + + diff --git a/app/src/main/res/layout/chat_wallpaper_selection_fragment_adapter_item.xml b/app/src/main/res/layout/chat_wallpaper_selection_fragment_adapter_item.xml index feee339148..273b5dd1e5 100644 --- a/app/src/main/res/layout/chat_wallpaper_selection_fragment_adapter_item.xml +++ b/app/src/main/res/layout/chat_wallpaper_selection_fragment_adapter_item.xml @@ -1,11 +1,26 @@ - \ No newline at end of file + android:layout_marginBottom="4dp"> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/conversation_activity.xml b/app/src/main/res/layout/conversation_activity.xml index 99a8e65560..6459a13d98 100644 --- a/app/src/main/res/layout/conversation_activity.xml +++ b/app/src/main/res/layout/conversation_activity.xml @@ -1,11 +1,31 @@ - + + + + + + @@ -56,7 +76,9 @@ + android:layout_height="0dp" + app:layout_constraintTop_toTopOf="@id/status_bar_guideline" + app:layout_constraintBottom_toBottomOf="@id/navigation_bar_guideline"> - + diff --git a/app/src/main/res/layout/conversation_banner_view.xml b/app/src/main/res/layout/conversation_banner_view.xml index d8c6303cb1..8e1edc0301 100644 --- a/app/src/main/res/layout/conversation_banner_view.xml +++ b/app/src/main/res/layout/conversation_banner_view.xml @@ -4,13 +4,13 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:padding="24dp" tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> @@ -35,8 +35,6 @@ android:layout_height="wrap_content" android:layout_marginTop="2dp" android:gravity="center" - android:paddingStart="16dp" - android:paddingEnd="16dp" android:textAppearance="@style/Signal.Text.MessageRequest.Subtitle" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -48,10 +46,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="15dp" - android:layout_marginBottom="15dp" android:gravity="center" - android:paddingStart="16dp" - android:paddingEnd="16dp" android:textAppearance="@style/Signal.Text.MessageRequest.Description" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/layout/conversation_input_panel.xml b/app/src/main/res/layout/conversation_input_panel.xml index 6d8fc15c61..cc0ecbb596 100644 --- a/app/src/main/res/layout/conversation_input_panel.xml +++ b/app/src/main/res/layout/conversation_input_panel.xml @@ -7,6 +7,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" + android:background="@color/signal_background_primary" android:clipChildren="false" android:clipToPadding="false"> diff --git a/app/src/main/res/layout/conversation_item_banner.xml b/app/src/main/res/layout/conversation_item_banner.xml index abbea8ca44..49967388b2 100644 --- a/app/src/main/res/layout/conversation_item_banner.xml +++ b/app/src/main/res/layout/conversation_item_banner.xml @@ -1,4 +1,9 @@ \ No newline at end of file + android:layout_height="wrap_content" + android:layout_margin="32dp" + android:paddingStart="16dp" + android:paddingEnd="16dp" + android:paddingTop="24dp" + android:paddingBottom="24dp"/> \ No newline at end of file diff --git a/app/src/main/res/layout/conversation_item_header.xml b/app/src/main/res/layout/conversation_item_header.xml index 7281038f8b..1416f8b14d 100644 --- a/app/src/main/res/layout/conversation_item_header.xml +++ b/app/src/main/res/layout/conversation_item_header.xml @@ -5,6 +5,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" + android:gravity="center" android:paddingTop="12dp" android:paddingBottom="13dp" android:paddingStart="28dp" @@ -12,11 +13,14 @@ diff --git a/app/src/main/res/layout/conversation_item_last_seen.xml b/app/src/main/res/layout/conversation_item_last_seen.xml index b68b5a50cb..3a04ecae01 100644 --- a/app/src/main/res/layout/conversation_item_last_seen.xml +++ b/app/src/main/res/layout/conversation_item_last_seen.xml @@ -19,6 +19,10 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" + android:paddingStart="10dp" + android:paddingEnd="10dp" + android:paddingTop="4dp" + android:paddingBottom="4dp" style="@style/Signal.Text.Caption" android:fontFamily="sans-serif-medium" android:textColor="@color/signal_text_primary" diff --git a/app/src/main/res/layout/conversation_item_received_multimedia.xml b/app/src/main/res/layout/conversation_item_received_multimedia.xml index 287863bdcb..8f2996d29d 100644 --- a/app/src/main/res/layout/conversation_item_received_multimedia.xml +++ b/app/src/main/res/layout/conversation_item_received_multimedia.xml @@ -61,10 +61,11 @@ android:id="@+id/group_sender_holder" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="@dimen/message_bubble_horizontal_padding" - android:layout_marginTop="@dimen/message_bubble_top_padding" - android:layout_marginEnd="@dimen/message_bubble_horizontal_padding" - android:layout_marginBottom="2dp" + android:layout_marginTop="3dp" + android:paddingTop="3dp" + android:paddingBottom="3dp" + android:paddingStart="@dimen/message_bubble_horizontal_padding" + android:paddingEnd="@dimen/message_bubble_horizontal_padding" android:orientation="horizontal" android:visibility="gone" tools:visibility="visible"> @@ -82,20 +83,6 @@ tools:text="+14152222222" tools:visibility="visible" /> - - @@ -82,20 +83,6 @@ tools:text="+14152222222" tools:visibility="visible" /> - - + android:paddingStart="26dp" + android:paddingEnd="26dp"> - - - + android:layout_gravity="center" + android:layout_marginBottom="3dp" + android:orientation="vertical" + android:gravity="center" + android:paddingTop="8dp" + android:paddingBottom="8dp" + android:paddingStart="10dp" + android:paddingEnd="10dp"> + + + + + + diff --git a/app/src/main/res/layout/conversation_reaction_scrubber.xml b/app/src/main/res/layout/conversation_reaction_scrubber.xml index 389d648265..b3fe2bf119 100644 --- a/app/src/main/res/layout/conversation_reaction_scrubber.xml +++ b/app/src/main/res/layout/conversation_reaction_scrubber.xml @@ -4,7 +4,9 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/conversation_reaction_scrubber" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="0dp" + app:layout_constraintTop_toTopOf="@+id/status_bar_guideline" + app:layout_constraintBottom_toBottomOf="@+id/navigation_bar_guideline" android:elevation="1000dp" android:visibility="gone" tools:visibility="visible"> diff --git a/app/src/main/res/layout/group_manage_fragment.xml b/app/src/main/res/layout/group_manage_fragment.xml index ee9e8671e6..1ee37d95b3 100644 --- a/app/src/main/res/layout/group_manage_fragment.xml +++ b/app/src/main/res/layout/group_manage_fragment.xml @@ -329,6 +329,18 @@ + + diff --git a/app/src/main/res/layout/recipient_manage_fragment.xml b/app/src/main/res/layout/recipient_manage_fragment.xml index 5d0ecb3fa1..3dc6d29217 100644 --- a/app/src/main/res/layout/recipient_manage_fragment.xml +++ b/app/src/main/res/layout/recipient_manage_fragment.xml @@ -472,37 +472,56 @@ app:layout_constraintTop_toBottomOf="@id/recipient_media_card"> + android:layout_height="wrap_content"> + + + + + + + + - - - diff --git a/app/src/main/res/layout/system_ui_guidelines.xml b/app/src/main/res/layout/system_ui_guidelines.xml new file mode 100644 index 0000000000..9b52deb8d8 --- /dev/null +++ b/app/src/main/res/layout/system_ui_guidelines.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/chat_wallpaper.xml b/app/src/main/res/navigation/chat_wallpaper.xml index 026f457acc..fec1d0fe4d 100644 --- a/app/src/main/res/navigation/chat_wallpaper.xml +++ b/app/src/main/res/navigation/chat_wallpaper.xml @@ -13,7 +13,11 @@ + app:destination="@id/chatWallpaperSelectionFragment" + app:enterAnim="@anim/slide_from_end" + app:exitAnim="@anim/slide_to_start" + app:popEnterAnim="@anim/slide_from_start" + app:popExitAnim="@anim/slide_to_end" /> @color/core_grey_25 @color/core_grey_05 + @color/transparent_black_80 + @color/core_white @color/core_grey_85 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 2f01abc2c9..7fe37a427a 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -50,5 +50,4 @@ #ff9BCFBD @color/core_ultramarine #ffA23474 - diff --git a/app/src/main/res/values/light_colors.xml b/app/src/main/res/values/light_colors.xml index 0f9471fe6f..5717ae8861 100644 --- a/app/src/main/res/values/light_colors.xml +++ b/app/src/main/res/values/light_colors.xml @@ -69,6 +69,8 @@ @color/core_grey_60 @color/core_grey_90 + @color/transparent_white_80 + @color/grey_600 @color/core_white diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7b9a7bd520..25418e175e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -701,6 +701,7 @@ Mute notifications Custom notifications Mentions + Chat wallpaper Until %1$s Off On @@ -752,6 +753,7 @@ This person is in your contacts Disappearing messages Chat color + Chat wallpaper Block Unblock View safety number