display outgoing messages
This commit is contained in:
parent
d52cbb8e8c
commit
5b777ef657
|
@ -426,7 +426,7 @@ public abstract class MessageDao {
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query(
|
@Query(
|
||||||
"SELECT c.accountId,m.id as id,type as"
|
"SELECT c.accountId,m.id as id,type as"
|
||||||
+ " chatType,sentAt,outgoing,toBare,toResource,fromBare,fromResource,senderIdentity"
|
+ " chatType,sentAt,outgoing,toBare,toResource,fromBare,fromResource,acknowledged,senderIdentity"
|
||||||
+ " as sender,(SELECT name FROM roster WHERE roster.accountId=c.accountId AND"
|
+ " as sender,(SELECT name FROM roster WHERE roster.accountId=c.accountId AND"
|
||||||
+ " roster.address=m.senderIdentity) as senderRosterName,(SELECT nick FROM nick"
|
+ " roster.address=m.senderIdentity) as senderRosterName,(SELECT nick FROM nick"
|
||||||
+ " WHERE nick.accountId=c.accountId AND nick.address=m.senderIdentity) as"
|
+ " WHERE nick.accountId=c.accountId AND nick.address=m.senderIdentity) as"
|
||||||
|
@ -456,7 +456,7 @@ public abstract class MessageDao {
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query(
|
@Query(
|
||||||
"SELECT c.accountId,m.id as id,type as"
|
"SELECT c.accountId,m.id as id,type as"
|
||||||
+ " chatType,sentAt,outgoing,toBare,toResource,fromBare,fromResource,senderIdentity"
|
+ " chatType,sentAt,outgoing,toBare,toResource,fromBare,fromResource,acknowledged,senderIdentity"
|
||||||
+ " as sender,(SELECT name FROM roster WHERE roster.accountId=c.accountId AND"
|
+ " as sender,(SELECT name FROM roster WHERE roster.accountId=c.accountId AND"
|
||||||
+ " roster.address=m.senderIdentity) as senderRosterName,(SELECT nick FROM nick"
|
+ " roster.address=m.senderIdentity) as senderRosterName,(SELECT nick FROM nick"
|
||||||
+ " WHERE nick.accountId=c.accountId AND nick.address=m.senderIdentity) as"
|
+ " WHERE nick.accountId=c.accountId AND nick.address=m.senderIdentity) as"
|
||||||
|
|
|
@ -3,6 +3,7 @@ package im.conversations.android.database.model;
|
||||||
import androidx.room.Relation;
|
import androidx.room.Relation;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.ImmutableSortedSet;
|
import com.google.common.collect.ImmutableSortedSet;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
@ -53,6 +54,7 @@ public class MessageWithContentReactions implements IndividualName, KnownSender
|
||||||
|
|
||||||
public Modification modification;
|
public Modification modification;
|
||||||
public long version;
|
public long version;
|
||||||
|
public boolean acknowledged;
|
||||||
public Long inReplyToMessageEntityId;
|
public Long inReplyToMessageEntityId;
|
||||||
public Encryption encryption;
|
public Encryption encryption;
|
||||||
public IdentityKey identityKey;
|
public IdentityKey identityKey;
|
||||||
|
@ -176,6 +178,27 @@ public class MessageWithContentReactions implements IndividualName, KnownSender
|
||||||
return new EncryptionTuple(this.encryption, this.trust);
|
return new EncryptionTuple(this.encryption, this.trust);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public State getState() {
|
||||||
|
if (outgoing) {
|
||||||
|
final var status =
|
||||||
|
Collections2.transform(
|
||||||
|
Collections2.filter(
|
||||||
|
this.states, s -> s != null && s.fromBare.equals(toBare)),
|
||||||
|
s -> s.type);
|
||||||
|
if (status.contains(StateType.DISPLAYED)) {
|
||||||
|
return State.READ;
|
||||||
|
} else if (status.contains(StateType.DELIVERED)) {
|
||||||
|
return State.DELIVERED;
|
||||||
|
} else if (acknowledged) {
|
||||||
|
return State.DELIVERED_TO_SERVER;
|
||||||
|
} else {
|
||||||
|
return State.NONE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return State.NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -251,4 +274,12 @@ public class MessageWithContentReactions implements IndividualName, KnownSender
|
||||||
this.trust = trust;
|
this.trust = trust;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum State {
|
||||||
|
NONE,
|
||||||
|
DELIVERED_TO_SERVER,
|
||||||
|
DELIVERED,
|
||||||
|
READ,
|
||||||
|
ERROR
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,11 @@ import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.databinding.BindingAdapter;
|
import androidx.databinding.BindingAdapter;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
|
import com.google.android.material.color.MaterialColors;
|
||||||
import com.google.android.material.textfield.TextInputEditText;
|
import com.google.android.material.textfield.TextInputEditText;
|
||||||
import com.google.android.material.textfield.TextInputLayout;
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
|
@ -144,4 +146,37 @@ public class BindingAdapters {
|
||||||
throw new IllegalArgumentException(String.format("Unknown encryption %s", encryption));
|
throw new IllegalArgumentException(String.format("Unknown encryption %s", encryption));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@BindingAdapter("state")
|
||||||
|
public static void setState(
|
||||||
|
final ImageView imageView, final MessageWithContentReactions.State state) {
|
||||||
|
if (state == null || state == MessageWithContentReactions.State.NONE) {
|
||||||
|
imageView.setVisibility(View.INVISIBLE);
|
||||||
|
} else {
|
||||||
|
@DrawableRes
|
||||||
|
final var drawableRes =
|
||||||
|
switch (state) {
|
||||||
|
case DELIVERED_TO_SERVER -> R.drawable.ic_check_24dp;
|
||||||
|
case DELIVERED, READ -> R.drawable.ic_done_all_24dp;
|
||||||
|
case ERROR -> R.drawable.ic_error_outline_24dp;
|
||||||
|
default -> throw new IllegalArgumentException(
|
||||||
|
String.format("State %s not implemented", state));
|
||||||
|
};
|
||||||
|
imageView.setImageResource(drawableRes);
|
||||||
|
if (state == MessageWithContentReactions.State.READ) {
|
||||||
|
// the two color candidates are colorTertiary and colorPrimary
|
||||||
|
// depending on the exact color scheme one might 'pop' more than the other
|
||||||
|
imageView.setImageTintList(
|
||||||
|
MaterialColors.getColorStateListOrNull(
|
||||||
|
imageView.getContext(),
|
||||||
|
com.google.android.material.R.attr.colorPrimary));
|
||||||
|
} else {
|
||||||
|
imageView.setImageTintList(
|
||||||
|
MaterialColors.getColorStateListOrNull(
|
||||||
|
imageView.getContext(),
|
||||||
|
com.google.android.material.R.attr.colorOnSurface));
|
||||||
|
}
|
||||||
|
imageView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
import im.conversations.android.R;
|
import im.conversations.android.R;
|
||||||
import im.conversations.android.database.model.MessageWithContentReactions;
|
import im.conversations.android.database.model.MessageWithContentReactions;
|
||||||
import im.conversations.android.databinding.ItemMessageReceivedBinding;
|
import im.conversations.android.databinding.ItemMessageReceivedBinding;
|
||||||
|
import im.conversations.android.databinding.ItemMessageSentBinding;
|
||||||
import im.conversations.android.ui.AvatarFetcher;
|
import im.conversations.android.ui.AvatarFetcher;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -19,6 +20,9 @@ public class MessageAdapter
|
||||||
extends PagingDataAdapter<
|
extends PagingDataAdapter<
|
||||||
MessageWithContentReactions, MessageAdapter.AbstractMessageViewHolder> {
|
MessageWithContentReactions, MessageAdapter.AbstractMessageViewHolder> {
|
||||||
|
|
||||||
|
private static final int VIEW_TYPE_RECEIVED = 0;
|
||||||
|
private static final int VIEW_TYPE_SENT = 1;
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(MessageAdapter.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(MessageAdapter.class);
|
||||||
|
|
||||||
public MessageAdapter(
|
public MessageAdapter(
|
||||||
|
@ -26,15 +30,29 @@ public class MessageAdapter
|
||||||
super(diffCallback);
|
super(diffCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(final int position) {
|
||||||
|
final var message = getItem(position);
|
||||||
|
if (message != null && message.outgoing) {
|
||||||
|
return VIEW_TYPE_SENT;
|
||||||
|
} else {
|
||||||
|
return VIEW_TYPE_RECEIVED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public AbstractMessageViewHolder onCreateViewHolder(
|
public AbstractMessageViewHolder onCreateViewHolder(
|
||||||
final @NonNull ViewGroup parent, final int viewType) {
|
final @NonNull ViewGroup parent, final int viewType) {
|
||||||
final var layoutInflater = LayoutInflater.from(parent.getContext());
|
final var layoutInflater = LayoutInflater.from(parent.getContext());
|
||||||
if (viewType == 0) {
|
if (viewType == VIEW_TYPE_RECEIVED) {
|
||||||
return new MessageReceivedViewHolder(
|
return new MessageReceivedViewHolder(
|
||||||
DataBindingUtil.inflate(
|
DataBindingUtil.inflate(
|
||||||
layoutInflater, R.layout.item_message_received, parent, false));
|
layoutInflater, R.layout.item_message_received, parent, false));
|
||||||
|
} else if (viewType == VIEW_TYPE_SENT) {
|
||||||
|
return new MessageSentViewHolder(
|
||||||
|
DataBindingUtil.inflate(
|
||||||
|
layoutInflater, R.layout.item_message_sent, parent, false));
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException(String.format("viewType %d not implemented", viewType));
|
throw new IllegalArgumentException(String.format("viewType %d not implemented", viewType));
|
||||||
}
|
}
|
||||||
|
@ -83,4 +101,19 @@ public class MessageAdapter
|
||||||
this.binding.setMessage(message);
|
this.binding.setMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class MessageSentViewHolder extends AbstractMessageViewHolder {
|
||||||
|
|
||||||
|
private final ItemMessageSentBinding binding;
|
||||||
|
|
||||||
|
public MessageSentViewHolder(@NonNull ItemMessageSentBinding binding) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setMessage(MessageWithContentReactions message) {
|
||||||
|
this.binding.setMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
10
app/src/main/res/drawable/ic_done_all_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_done_all_24dp.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zM22.24,5.59L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z" />
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_error_outline_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_error_outline_24dp.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" />
|
||||||
|
</vector>
|
88
app/src/main/res/layout/item_message_sent.xml
Normal file
88
app/src/main/res/layout/item_message_sent.xml
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingVertical="6dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="8dp"
|
||||||
|
android:background="@drawable/background_message_received"
|
||||||
|
android:backgroundTint="?colorTertiaryContainer"
|
||||||
|
android:minHeight="40dp"
|
||||||
|
android:padding="8dp"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="1.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textContent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{message.textContent}"
|
||||||
|
android:textAppearance="?textAppearanceBodyMedium"
|
||||||
|
android:textColor="?colorOnSecondaryContainer"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Fusce vitae!" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/time"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2sp"
|
||||||
|
android:layout_marginEnd="4sp"
|
||||||
|
android:textAppearance="?textAppearanceLabelSmall"
|
||||||
|
android:textColor="?colorOnSurface"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/encryption"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/content"
|
||||||
|
app:time="@{message.sentAt}"
|
||||||
|
tools:text="11:42 PM" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/encryption"
|
||||||
|
android:layout_width="12dp"
|
||||||
|
android:layout_height="12dp"
|
||||||
|
android:layout_marginEnd="4sp"
|
||||||
|
android:src="@drawable/ic_lock_outline_24dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:encryption="@{message.getEncryption()}"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/time"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/status"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/time"
|
||||||
|
app:tint="?colorOnSurface" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/status"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:src="@drawable/ic_done_all_24dp"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/time"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/content"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/time"
|
||||||
|
app:state="@{message.state}" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<import type="android.view.View" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="message"
|
||||||
|
type="im.conversations.android.database.model.MessageWithContentReactions" />
|
||||||
|
</data>
|
||||||
|
</layout>
|
Loading…
Reference in a new issue