flash background after scrolling to message
This commit is contained in:
parent
4f654044b4
commit
acfcde8416
|
@ -115,12 +115,23 @@ public final class MessageWithContentReactions
|
|||
return Iterables.tryFind(this.contents, c -> c.type == PartType.FILE).isPresent();
|
||||
}
|
||||
|
||||
public boolean hasDownloadButton() {
|
||||
return hasPreview();
|
||||
}
|
||||
|
||||
public boolean hasTextContent() {
|
||||
return Iterables.tryFind(this.contents, c -> c.type == PartType.TEXT).isPresent();
|
||||
}
|
||||
|
||||
public boolean hasInReplyTo() {
|
||||
return this.inReplyTo != null;
|
||||
}
|
||||
|
||||
public Instant inReplyToSentAt() {
|
||||
return this.inReplyTo == null ? null : this.inReplyTo.sentAt;
|
||||
public EmbeddedSentAt inReplyToSentAt() {
|
||||
if (this.inReplyTo == null) {
|
||||
return null;
|
||||
}
|
||||
return new EmbeddedSentAt(this.sentAt, this.inReplyTo.sentAt);
|
||||
}
|
||||
|
||||
public String inReplyToSender() {
|
||||
|
@ -152,9 +163,6 @@ public final class MessageWithContentReactions
|
|||
|
||||
public AvatarWithAccount getAvatar() {
|
||||
final var address = getAddressWithName();
|
||||
if (address == null) {
|
||||
return null;
|
||||
}
|
||||
if (isKnownSender()) {
|
||||
if (this.senderAvatar != null) {
|
||||
return new AvatarWithAccount(accountId, address, AvatarType.PEP, this.senderAvatar);
|
||||
|
@ -318,4 +326,14 @@ public final class MessageWithContentReactions
|
|||
READ,
|
||||
ERROR
|
||||
}
|
||||
|
||||
public static class EmbeddedSentAt {
|
||||
public final Instant sentAt;
|
||||
public final Instant embeddedSentAt;
|
||||
|
||||
public EmbeddedSentAt(Instant sentAt, Instant embeddedSentAt) {
|
||||
this.sentAt = sentAt;
|
||||
this.embeddedSentAt = embeddedSentAt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,30 +64,45 @@ public class BindingAdapters {
|
|||
if (instant == null || instant.getEpochSecond() <= 0) {
|
||||
textView.setVisibility(View.GONE);
|
||||
} else {
|
||||
final Context context = textView.getContext();
|
||||
final Instant now = Instant.now();
|
||||
textView.setVisibility(View.VISIBLE);
|
||||
if (sameDay(instant, now) || now.minus(SIX_HOURS).isBefore(instant)) {
|
||||
textView.setText(
|
||||
DateUtils.formatDateTime(
|
||||
context, instant.toEpochMilli(), DateUtils.FORMAT_SHOW_TIME));
|
||||
} else if (sameYear(instant, now) || now.minus(THREE_MONTH).isBefore(instant)) {
|
||||
textView.setText(
|
||||
DateUtils.formatDateTime(
|
||||
context,
|
||||
instant.toEpochMilli(),
|
||||
DateUtils.FORMAT_SHOW_DATE
|
||||
| DateUtils.FORMAT_NO_YEAR
|
||||
| DateUtils.FORMAT_ABBREV_ALL));
|
||||
} else {
|
||||
textView.setText(
|
||||
DateUtils.formatDateTime(
|
||||
context,
|
||||
instant.toEpochMilli(),
|
||||
DateUtils.FORMAT_SHOW_DATE
|
||||
| DateUtils.FORMAT_NO_MONTH_DAY
|
||||
| DateUtils.FORMAT_ABBREV_ALL));
|
||||
}
|
||||
setDatetime(textView, Instant.now(), instant);
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("datetime")
|
||||
public static void setDatetime(
|
||||
final TextView textView,
|
||||
final MessageWithContentReactions.EmbeddedSentAt embeddedSentAt) {
|
||||
if (embeddedSentAt == null || embeddedSentAt.embeddedSentAt.getEpochSecond() <= 0) {
|
||||
textView.setVisibility(View.GONE);
|
||||
} else {
|
||||
setDatetime(textView, embeddedSentAt.sentAt, embeddedSentAt.embeddedSentAt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setDatetime(
|
||||
final TextView textView, final Instant now, final Instant instant) {
|
||||
final Context context = textView.getContext();
|
||||
textView.setVisibility(View.VISIBLE);
|
||||
if (sameDay(instant, now) || now.minus(SIX_HOURS).isBefore(instant)) {
|
||||
textView.setText(
|
||||
DateUtils.formatDateTime(
|
||||
context, instant.toEpochMilli(), DateUtils.FORMAT_SHOW_TIME));
|
||||
} else if (sameYear(instant, now) || now.minus(THREE_MONTH).isBefore(instant)) {
|
||||
textView.setText(
|
||||
DateUtils.formatDateTime(
|
||||
context,
|
||||
instant.toEpochMilli(),
|
||||
DateUtils.FORMAT_SHOW_DATE
|
||||
| DateUtils.FORMAT_NO_YEAR
|
||||
| DateUtils.FORMAT_ABBREV_ALL));
|
||||
} else {
|
||||
textView.setText(
|
||||
DateUtils.formatDateTime(
|
||||
context,
|
||||
instant.toEpochMilli(),
|
||||
DateUtils.FORMAT_SHOW_DATE
|
||||
| DateUtils.FORMAT_NO_MONTH_DAY
|
||||
| DateUtils.FORMAT_ABBREV_ALL));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import im.conversations.android.databinding.ItemMessageReceivedBinding;
|
|||
import im.conversations.android.databinding.ItemMessageSentBinding;
|
||||
import im.conversations.android.databinding.ItemMessageSeparatorBinding;
|
||||
import im.conversations.android.ui.AvatarFetcher;
|
||||
import im.conversations.android.ui.graphics.drawable.FlashBackgroundDrawable;
|
||||
import java.util.function.Consumer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -93,6 +94,11 @@ public class MessageAdapter
|
|||
@NonNull AbstractMessageViewHolder holder,
|
||||
@NonNull final MessageWithContentReactions message) {
|
||||
holder.setItem(message);
|
||||
if (holder.itemView.getBackground() instanceof FlashBackgroundDrawable backgroundDrawable) {
|
||||
if (backgroundDrawable.needsReset(message.id)) {
|
||||
holder.itemView.setBackground(null);
|
||||
}
|
||||
}
|
||||
final var inReplyTo = message.inReplyTo;
|
||||
if (holder instanceof MessageReceivedViewHolder messageReceivedViewHolder) {
|
||||
if (inReplyTo != null) {
|
||||
|
|
|
@ -21,6 +21,7 @@ import im.conversations.android.ui.RecyclerViewScroller;
|
|||
import im.conversations.android.ui.adapter.MessageAdapter;
|
||||
import im.conversations.android.ui.adapter.MessageAdapterItems;
|
||||
import im.conversations.android.ui.adapter.MessageComparator;
|
||||
import im.conversations.android.ui.graphics.drawable.FlashBackgroundDrawable;
|
||||
import im.conversations.android.ui.model.ChatViewModel;
|
||||
import im.conversations.android.util.MainThreadExecutor;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -108,6 +109,7 @@ public class ChatFragment extends Fragment {
|
|||
}
|
||||
|
||||
private void scrollToMessageId(final long messageId) {
|
||||
// TODO do not scroll if view is fully visible
|
||||
LOGGER.info("scrollToMessageId({})", messageId);
|
||||
this.chatViewModel.setShowDateSeparators(false);
|
||||
final var future = this.chatViewModel.getMessagePosition(messageId);
|
||||
|
@ -117,7 +119,11 @@ public class ChatFragment extends Fragment {
|
|||
@Override
|
||||
public void onSuccess(final @NonNull Integer position) {
|
||||
recyclerViewScroller.scrollToPosition(
|
||||
position, () -> chatViewModel.setShowDateSeparators(true));
|
||||
position,
|
||||
() -> {
|
||||
chatViewModel.setShowDateSeparators(true);
|
||||
flashBackgroundAtPosition(position, messageId);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -128,4 +134,15 @@ public class ChatFragment extends Fragment {
|
|||
},
|
||||
MainThreadExecutor.getInstance());
|
||||
}
|
||||
|
||||
private void flashBackgroundAtPosition(final int position, final long messageId) {
|
||||
final var layoutManager = this.binding.messages.getLayoutManager();
|
||||
if (layoutManager instanceof LinearLayoutManager llm) {
|
||||
final var view = llm.findViewByPosition(position);
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
FlashBackgroundDrawable.flashBackground(view, messageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package im.conversations.android.ui.graphics.drawable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.AnimationDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.View;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
|
||||
public class FlashBackgroundDrawable extends AnimationDrawable {
|
||||
|
||||
private final long messageId;
|
||||
|
||||
private FlashBackgroundDrawable(final Context context, final long messageId) {
|
||||
this.messageId = messageId;
|
||||
@ColorInt
|
||||
int backgroundColor =
|
||||
MaterialColors.getColor(
|
||||
context,
|
||||
com.google.android.material.R.attr.colorSurfaceVariant,
|
||||
"colorSurfaceVariant not found");
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
this.addFrame(new ColorDrawable(backgroundColor), 250);
|
||||
this.addFrame(new ColorDrawable(android.graphics.Color.TRANSPARENT), 250);
|
||||
}
|
||||
this.setEnterFadeDuration(125);
|
||||
this.setExitFadeDuration(125);
|
||||
this.setOneShot(true);
|
||||
}
|
||||
|
||||
public boolean needsReset(final long messageId) {
|
||||
return this.messageId != messageId || !this.isRunning();
|
||||
}
|
||||
|
||||
public static void flashBackground(@NonNull final View view, final long messageId) {
|
||||
final var animationDrawable = new FlashBackgroundDrawable(view.getContext(), messageId);
|
||||
view.setBackground(animationDrawable);
|
||||
view.post(animationDrawable::start);
|
||||
}
|
||||
}
|
|
@ -44,7 +44,8 @@
|
|||
android:visibility="@{message.hasInReplyTo ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/embeddedMessageSender"
|
||||
|
@ -86,12 +87,29 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/textContentWrapper"
|
||||
android:visibility="@{message.hasPreview ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/downloadButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_max="@dimen/message_preview_max_height"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/embeddedMessage"
|
||||
app:layout_constraintWidth_max="@dimen/message_preview_max_width" />
|
||||
app:layout_constraintWidth_max="@dimen/message_preview_max_width"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/downloadButton"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:text="@string/check_x_filesize_on_host"
|
||||
android:visibility="@{message.hasDownloadButton ? View.VISIBLE : View.GONE}"
|
||||
app:icon="@drawable/ic_download_24dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/textContentWrapper"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/image"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/textContentWrapper"
|
||||
|
@ -99,11 +117,11 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
android:visibility="visible"
|
||||
android:visibility="@{message.hasTextContent ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/image">
|
||||
app:layout_constraintTop_toBottomOf="@+id/downloadButton">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textContent"
|
||||
|
@ -114,8 +132,6 @@
|
|||
android:textColor="?colorOnSecondaryContainer"
|
||||
tools:text="Quisque sit amet metus faucibus, egestas est eu, hendrerit mauris. Suspendisse pretium nisl purus, vitae vestibulum sapien rhoncus nec. Quisque molestie ante felis, vel dapibus ex mattis a. Morbi venenatis vestibulum neque, vel ornare sapien. Aliquam erat volutpat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. " />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
android:background="@drawable/background_message_bubble"
|
||||
android:backgroundTint="?colorTertiaryContainer"
|
||||
android:minHeight="40dp"
|
||||
android:padding="8dp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
|
@ -34,7 +33,8 @@
|
|||
android:visibility="@{message.hasInReplyTo ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/embeddedMessageSender"
|
||||
|
@ -76,12 +76,29 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/textContentWrapper"
|
||||
android:visibility="@{message.hasPreview ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/downloadButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_max="@dimen/message_preview_max_height"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/embeddedMessage"
|
||||
app:layout_constraintWidth_max="@dimen/message_preview_max_width" />
|
||||
app:layout_constraintWidth_max="@dimen/message_preview_max_width"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/downloadButton"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:text="@string/check_x_filesize_on_host"
|
||||
android:visibility="@{message.hasDownloadButton ? View.VISIBLE : View.GONE}"
|
||||
app:icon="@drawable/ic_download_24dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/textContentWrapper"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/image"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/textContentWrapper"
|
||||
|
@ -89,11 +106,11 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
android:visibility="visible"
|
||||
android:visibility="@{message.hasTextContent ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/image">
|
||||
app:layout_constraintTop_toBottomOf="@+id/downloadButton">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textContent"
|
||||
|
|
Loading…
Reference in a new issue