Show timer change notices in private conversations

This commit is contained in:
Torsten Grote
2020-12-05 09:52:08 -03:00
committed by akwizgran
parent 4a73daa214
commit 04ef837307
18 changed files with 284 additions and 71 deletions

View File

@@ -13,6 +13,8 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.util.BriarAdapter; import org.briarproject.briar.android.util.BriarAdapter;
import org.briarproject.briar.android.util.ItemReturningAdapter; import org.briarproject.briar.android.util.ItemReturningAdapter;
import java.util.Collection;
import androidx.annotation.LayoutRes; import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.recyclerview.selection.SelectionTracker; import androidx.recyclerview.selection.SelectionTracker;
@@ -20,13 +22,14 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView.RecycledViewPool; import androidx.recyclerview.widget.RecyclerView.RecycledViewPool;
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
@NotNullByDefault @NotNullByDefault
class ConversationAdapter class ConversationAdapter
extends BriarAdapter<ConversationItem, ConversationItemViewHolder> extends BriarAdapter<ConversationItem, ConversationItemViewHolder>
implements ItemReturningAdapter<ConversationItem> { implements ItemReturningAdapter<ConversationItem> {
private ConversationListener listener; private final ConversationListener listener;
private final RecycledViewPool imageViewPool; private final RecycledViewPool imageViewPool;
private final ImageItemDecoration imageItemDecoration; private final ImageItemDecoration imageItemDecoration;
@Nullable @Nullable
@@ -65,22 +68,20 @@ class ConversationAdapter
@LayoutRes int type) { @LayoutRes int type) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate( View v = LayoutInflater.from(viewGroup.getContext()).inflate(
type, viewGroup, false); type, viewGroup, false);
switch (type) { if (type == R.layout.list_item_conversation_msg_in) {
case R.layout.list_item_conversation_msg_in: return new ConversationMessageViewHolder(v, listener, true,
return new ConversationMessageViewHolder(v, listener, true, imageViewPool, imageItemDecoration);
imageViewPool, imageItemDecoration); } else if (type == R.layout.list_item_conversation_msg_out) {
case R.layout.list_item_conversation_msg_out: return new ConversationMessageViewHolder(v, listener, false,
return new ConversationMessageViewHolder(v, listener, false, imageViewPool, imageItemDecoration);
imageViewPool, imageItemDecoration); } else if (type == R.layout.list_item_conversation_notice_in) {
case R.layout.list_item_conversation_notice_in: return new ConversationNoticeViewHolder(v, listener, true);
return new ConversationNoticeViewHolder(v, listener, true); } else if (type == R.layout.list_item_conversation_notice_out) {
case R.layout.list_item_conversation_notice_out: return new ConversationNoticeViewHolder(v, listener, false);
return new ConversationNoticeViewHolder(v, listener, false); } else if (type == R.layout.list_item_conversation_request) {
case R.layout.list_item_conversation_request: return new ConversationRequestViewHolder(v, listener, true);
return new ConversationRequestViewHolder(v, listener, true);
default:
throw new IllegalArgumentException("Unknown ConversationItem");
} }
throw new IllegalArgumentException("Unknown ConversationItem");
} }
@Override @Override
@@ -107,22 +108,53 @@ class ConversationAdapter
return c1.equals(c2); return c1.equals(c2);
} }
@Override
public void add(ConversationItem item) {
items.beginBatchedUpdates();
items.add(item);
updateTimersInBatch(true);
items.endBatchedUpdates();
}
@Override
public void addAll(Collection<ConversationItem> itemsToAdd) {
items.beginBatchedUpdates();
items.addAll(itemsToAdd);
updateTimersInBatch(false);
items.endBatchedUpdates();
}
private void updateTimersInBatch(boolean updateItems) {
long lastTimerIncoming = NO_AUTO_DELETE_TIMER;
long lastTimerOutgoing = NO_AUTO_DELETE_TIMER;
for (int i = 0; i < items.size(); i++) {
ConversationItem c = items.get(i);
boolean itemChanged;
boolean timerChanged;
boolean timerMirrored;
if (c.isIncoming()) {
timerChanged = lastTimerIncoming != c.getAutoDeleteTimer();
timerMirrored = timerChanged &&
lastTimerOutgoing == c.getAutoDeleteTimer();
lastTimerIncoming = c.getAutoDeleteTimer();
} else {
timerChanged = lastTimerOutgoing != c.getAutoDeleteTimer();
timerMirrored = timerChanged &&
lastTimerIncoming == c.getAutoDeleteTimer();
lastTimerOutgoing = c.getAutoDeleteTimer();
}
itemChanged = c.setTimerNoticeVisible(timerChanged);
itemChanged |= c.setTimerMirrored(timerMirrored);
if (itemChanged && updateItems) items.updateItemAt(i, c);
}
}
void setSelectionTracker(SelectionTracker<String> tracker) { void setSelectionTracker(SelectionTracker<String> tracker) {
this.tracker = tracker; this.tracker = tracker;
} }
@Nullable
ConversationItem getLastItem() {
if (items.size() > 0) {
return items.get(items.size() - 1);
} else {
return null;
}
}
SparseArray<ConversationItem> getOutgoingMessages() { SparseArray<ConversationItem> getOutgoingMessages() {
SparseArray<ConversationItem> messages = new SparseArray<>(); SparseArray<ConversationItem> messages = new SparseArray<>();
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {
ConversationItem item = items.get(i); ConversationItem item = items.get(i);
if (!item.isIncoming()) { if (!item.isIncoming()) {

View File

@@ -9,6 +9,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
import androidx.annotation.LayoutRes; import androidx.annotation.LayoutRes;
import androidx.lifecycle.LiveData;
import static org.briarproject.bramble.util.StringUtils.toHexString; import static org.briarproject.bramble.util.StringUtils.toHexString;
@@ -24,9 +25,11 @@ abstract class ConversationItem {
private final GroupId groupId; private final GroupId groupId;
private final long time, autoDeleteTimer; private final long time, autoDeleteTimer;
private final boolean isIncoming; private final boolean isIncoming;
private boolean read, sent, seen; private final LiveData<String> contactName;
private boolean read, sent, seen, showTimerNotice, timerMirrored;
ConversationItem(@LayoutRes int layoutRes, ConversationMessageHeader h) { ConversationItem(@LayoutRes int layoutRes, ConversationMessageHeader h,
LiveData<String> contactName) {
this.layoutRes = layoutRes; this.layoutRes = layoutRes;
this.text = null; this.text = null;
this.id = h.getId(); this.id = h.getId();
@@ -37,6 +40,9 @@ abstract class ConversationItem {
this.sent = h.isSent(); this.sent = h.isSent();
this.seen = h.isSeen(); this.seen = h.isSeen();
this.isIncoming = !h.isLocal(); this.isIncoming = !h.isLocal();
this.contactName = contactName;
this.showTimerNotice = false;
this.timerMirrored = false;
} }
@LayoutRes @LayoutRes
@@ -116,4 +122,44 @@ abstract class ConversationItem {
return isIncoming; return isIncoming;
} }
public LiveData<String> getContactName() {
return contactName;
}
/**
* Set this to true when {@link #getAutoDeleteTimer()} has changed
* since the last message from the same peer.
*
* @return true if the value was set, false if it was already set.
*/
boolean setTimerNoticeVisible(boolean visible) {
if (this.showTimerNotice != visible) {
this.showTimerNotice = visible;
return true;
}
return false;
}
boolean isTimerNoticeVisible() {
return showTimerNotice;
}
/**
* Set this to true when {@link #getAutoDeleteTimer()} has changed
* to the same timer of the last message
* from the other peer in this conversation.
*
* @return true if the value was set, false if it was already set.
*/
public boolean setTimerMirrored(boolean timerMirrored) {
if (this.timerMirrored != timerMirrored) {
this.timerMirrored = timerMirrored;
return true;
}
return false;
}
public boolean wasTimerMirrored() {
return timerMirrored;
}
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.briar.android.conversation; package org.briarproject.briar.android.conversation;
import android.content.Context;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
@@ -27,7 +28,7 @@ abstract class ConversationItemViewHolder extends ViewHolder {
protected final ConstraintLayout layout; protected final ConstraintLayout layout;
@Nullable @Nullable
private final OutItemViewHolder outViewHolder; private final OutItemViewHolder outViewHolder;
private final TextView text; private final TextView topNotice, text;
protected final TextView time; protected final TextView time;
private final View bomb; private final View bomb;
@Nullable @Nullable
@@ -39,6 +40,7 @@ abstract class ConversationItemViewHolder extends ViewHolder {
this.listener = listener; this.listener = listener;
this.outViewHolder = isIncoming ? null : new OutItemViewHolder(v); this.outViewHolder = isIncoming ? null : new OutItemViewHolder(v);
root = v; root = v;
topNotice = v.findViewById(R.id.topNotice);
layout = v.findViewById(R.id.layout); layout = v.findViewById(R.id.layout);
text = v.findViewById(R.id.text); text = v.findViewById(R.id.text);
time = v.findViewById(R.id.time); time = v.findViewById(R.id.time);
@@ -50,6 +52,8 @@ abstract class ConversationItemViewHolder extends ViewHolder {
itemKey = item.getKey(); itemKey = item.getKey();
root.setActivated(selected); root.setActivated(selected);
setTopNotice(item);
if (item.getText() != null) { if (item.getText() != null) {
text.setText(trim(item.getText())); text.setText(trim(item.getText()));
} }
@@ -72,4 +76,42 @@ abstract class ConversationItemViewHolder extends ViewHolder {
return itemKey; return itemKey;
} }
private void setTopNotice(ConversationItem item) {
if (item.isTimerNoticeVisible()) {
Context ctx = itemView.getContext();
topNotice.setVisibility(VISIBLE);
boolean enabled = item.getAutoDeleteTimer() != NO_AUTO_DELETE_TIMER;
String text;
if (item.isIncoming()) {
String name = item.getContactName().getValue();
if (item.wasTimerMirrored()) {
int strRes = enabled ?
R.string.auto_delete_msg_contact_enabled_mirrored :
R.string.auto_delete_msg_contact_disabled_mirrored;
text = ctx.getString(strRes, name);
} else {
int strRes = enabled ?
R.string.auto_delete_msg_contact_enabled :
R.string.auto_delete_msg_contact_disabled;
text = ctx.getString(strRes, name, name);
}
} else {
int strRes;
if (item.wasTimerMirrored()) {
strRes = enabled ?
R.string.auto_delete_msg_you_enabled_mirrored :
R.string.auto_delete_msg_you_disabled_mirrored;
} else {
strRes = enabled ?
R.string.auto_delete_msg_you_enabled :
R.string.auto_delete_msg_you_disabled;
}
text = ctx.getString(strRes);
}
topNotice.setText(text);
} else {
topNotice.setVisibility(GONE);
}
}
} }

View File

@@ -10,6 +10,7 @@ import javax.annotation.concurrent.NotThreadSafe;
import androidx.annotation.LayoutRes; import androidx.annotation.LayoutRes;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.lifecycle.LiveData;
@NotThreadSafe @NotThreadSafe
@NotNullByDefault @NotNullByDefault
@@ -18,8 +19,8 @@ class ConversationMessageItem extends ConversationItem {
private final List<AttachmentItem> attachments; private final List<AttachmentItem> attachments;
ConversationMessageItem(@LayoutRes int layoutRes, PrivateMessageHeader h, ConversationMessageItem(@LayoutRes int layoutRes, PrivateMessageHeader h,
List<AttachmentItem> attachments) { LiveData<String> contactName, List<AttachmentItem> attachments) {
super(layoutRes, h); super(layoutRes, h, contactName);
this.attachments = attachments; this.attachments = attachments;
} }

View File

@@ -8,6 +8,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
import androidx.annotation.LayoutRes; import androidx.annotation.LayoutRes;
import androidx.lifecycle.LiveData;
@NotThreadSafe @NotThreadSafe
@NotNullByDefault @NotNullByDefault
@@ -17,15 +18,15 @@ class ConversationNoticeItem extends ConversationItem {
private final String msgText; private final String msgText;
ConversationNoticeItem(@LayoutRes int layoutRes, String text, ConversationNoticeItem(@LayoutRes int layoutRes, String text,
ConversationRequest r) { LiveData<String> contactName, ConversationRequest<?> r) {
super(layoutRes, r); super(layoutRes, r, contactName);
this.text = text; this.text = text;
this.msgText = r.getText(); this.msgText = r.getText();
} }
ConversationNoticeItem(@LayoutRes int layoutRes, String text, ConversationNoticeItem(@LayoutRes int layoutRes, String text,
ConversationResponse r) { LiveData<String> contactName, ConversationResponse r) {
super(layoutRes, r); super(layoutRes, r, contactName);
this.text = text; this.text = text;
this.msgText = null; this.msgText = null;
} }

View File

@@ -11,6 +11,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
import androidx.annotation.LayoutRes; import androidx.annotation.LayoutRes;
import androidx.lifecycle.LiveData;
@NotThreadSafe @NotThreadSafe
@NotNullByDefault @NotNullByDefault
@@ -26,14 +27,15 @@ class ConversationRequestItem extends ConversationNoticeItem {
private boolean answered; private boolean answered;
ConversationRequestItem(@LayoutRes int layoutRes, String text, ConversationRequestItem(@LayoutRes int layoutRes, String text,
RequestType type, ConversationRequest r) { LiveData<String> contactName, RequestType type,
super(layoutRes, text, r); ConversationRequest<?> r) {
super(layoutRes, text, contactName, r);
this.requestType = type; this.requestType = type;
this.sessionId = r.getSessionId(); this.sessionId = r.getSessionId();
this.answered = r.wasAnswered(); this.answered = r.wasAnswered();
if (r instanceof InvitationRequest) { if (r instanceof InvitationRequest) {
this.requestedGroupId = ((Shareable) r.getNameable()).getId(); this.requestedGroupId = ((Shareable) r.getNameable()).getId();
this.canBeOpened = ((InvitationRequest) r).canBeOpened(); this.canBeOpened = ((InvitationRequest<?>) r).canBeOpened();
} else { } else {
this.requestedGroupId = null; this.requestedGroupId = null;
this.canBeOpened = false; this.canBeOpened = false;

View File

@@ -60,10 +60,12 @@ class ConversationVisitor implements
} }
if (h.isLocal()) { if (h.isLocal()) {
item = new ConversationMessageItem( item = new ConversationMessageItem(
R.layout.list_item_conversation_msg_out, h, attachments); R.layout.list_item_conversation_msg_out, h, contactName,
attachments);
} else { } else {
item = new ConversationMessageItem( item = new ConversationMessageItem(
R.layout.list_item_conversation_msg_in, h, attachments); R.layout.list_item_conversation_msg_in, h, contactName,
attachments);
} }
if (h.hasText()) { if (h.hasText()) {
String text = textCache.getText(h.getId()); String text = textCache.getText(h.getId());
@@ -79,13 +81,15 @@ class ConversationVisitor implements
String text = ctx.getString(R.string.blogs_sharing_invitation_sent, String text = ctx.getString(R.string.blogs_sharing_invitation_sent,
r.getName(), contactName.getValue()); r.getName(), contactName.getValue());
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_out, text, r); R.layout.list_item_conversation_notice_out, text,
contactName, r);
} else { } else {
String text = ctx.getString( String text = ctx.getString(
R.string.blogs_sharing_invitation_received, R.string.blogs_sharing_invitation_received,
contactName.getValue(), r.getName()); contactName.getValue(), r.getName());
return new ConversationRequestItem( return new ConversationRequestItem(
R.layout.list_item_conversation_request, text, BLOG, r); R.layout.list_item_conversation_request, text, contactName,
BLOG, r);
} }
} }
@@ -104,7 +108,8 @@ class ConversationVisitor implements
contactName.getValue()); contactName.getValue());
} }
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_out, text, r); R.layout.list_item_conversation_notice_out, text,
contactName, r);
} else { } else {
String text; String text;
if (r.wasAccepted()) { if (r.wasAccepted()) {
@@ -117,7 +122,8 @@ class ConversationVisitor implements
contactName.getValue()); contactName.getValue());
} }
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_in, text, r); R.layout.list_item_conversation_notice_in, text,
contactName, r);
} }
} }
@@ -128,13 +134,15 @@ class ConversationVisitor implements
String text = ctx.getString(R.string.forum_invitation_sent, String text = ctx.getString(R.string.forum_invitation_sent,
r.getName(), contactName.getValue()); r.getName(), contactName.getValue());
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_out, text, r); R.layout.list_item_conversation_notice_out, text,
contactName, r);
} else { } else {
String text = ctx.getString( String text = ctx.getString(
R.string.forum_invitation_received, R.string.forum_invitation_received,
contactName.getValue(), r.getName()); contactName.getValue(), r.getName());
return new ConversationRequestItem( return new ConversationRequestItem(
R.layout.list_item_conversation_request, text, FORUM, r); R.layout.list_item_conversation_request, text, contactName,
FORUM, r);
} }
} }
@@ -153,7 +161,8 @@ class ConversationVisitor implements
contactName.getValue()); contactName.getValue());
} }
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_out, text, r); R.layout.list_item_conversation_notice_out, text,
contactName, r);
} else { } else {
String text; String text;
if (r.wasAccepted()) { if (r.wasAccepted()) {
@@ -166,7 +175,8 @@ class ConversationVisitor implements
contactName.getValue()); contactName.getValue());
} }
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_in, text, r); R.layout.list_item_conversation_notice_in, text,
contactName, r);
} }
} }
@@ -178,13 +188,15 @@ class ConversationVisitor implements
R.string.groups_invitations_invitation_sent, R.string.groups_invitations_invitation_sent,
contactName.getValue(), r.getName()); contactName.getValue(), r.getName());
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_out, text, r); R.layout.list_item_conversation_notice_out, text,
contactName, r);
} else { } else {
String text = ctx.getString( String text = ctx.getString(
R.string.groups_invitations_invitation_received, R.string.groups_invitations_invitation_received,
contactName.getValue(), r.getName()); contactName.getValue(), r.getName());
return new ConversationRequestItem( return new ConversationRequestItem(
R.layout.list_item_conversation_request, text, GROUP, r); R.layout.list_item_conversation_request, text, contactName,
GROUP, r);
} }
} }
@@ -203,7 +215,8 @@ class ConversationVisitor implements
contactName.getValue()); contactName.getValue());
} }
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_out, text, r); R.layout.list_item_conversation_notice_out, text,
contactName, r);
} else { } else {
String text; String text;
if (r.wasAccepted()) { if (r.wasAccepted()) {
@@ -216,7 +229,8 @@ class ConversationVisitor implements
contactName.getValue()); contactName.getValue());
} }
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_in, text, r); R.layout.list_item_conversation_notice_in, text,
contactName, r);
} }
} }
@@ -227,7 +241,8 @@ class ConversationVisitor implements
String text = ctx.getString(R.string.introduction_request_sent, String text = ctx.getString(R.string.introduction_request_sent,
contactName.getValue(), name); contactName.getValue(), name);
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_out, text, r); R.layout.list_item_conversation_notice_out, text,
contactName, r);
} else { } else {
String text; String text;
if (r.wasAnswered()) { if (r.wasAnswered()) {
@@ -243,7 +258,7 @@ class ConversationVisitor implements
contactName.getValue(), name); contactName.getValue(), name);
} }
return new ConversationRequestItem( return new ConversationRequestItem(
R.layout.list_item_conversation_request, text, R.layout.list_item_conversation_request, text, contactName,
INTRODUCTION, r); INTRODUCTION, r);
} }
} }
@@ -268,7 +283,8 @@ class ConversationVisitor implements
introducedAuthor); introducedAuthor);
} }
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_out, text, r); R.layout.list_item_conversation_notice_out, text,
contactName, r);
} else { } else {
String text; String text;
if (r.wasAccepted()) { if (r.wasAccepted()) {
@@ -288,7 +304,8 @@ class ConversationVisitor implements
introducedAuthor); introducedAuthor);
} }
return new ConversationNoticeItem( return new ConversationNoticeItem(
R.layout.list_item_conversation_notice_in, text, r); R.layout.list_item_conversation_notice_in, text,
contactName, r);
} }
} }

View File

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- This layout is only used indirectly by cloning and setting its ConstraintSet -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"

View File

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- This layout is only used indirectly by cloning and setting its ConstraintSet -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"

View File

@@ -1,14 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/list_item_background_selectable"> android:background="@drawable/list_item_background_selectable"
android:orientation="vertical">
<!-- <!--
We need to wrap the actual layout, because We need to wrap the actual layout, because we want to
* we want to clone the ConstraintLayout's constraints in the ViewHolder * clone the ConstraintLayout's constraints in the ViewHolder
* we want to have a selectable frame around the message bubble * have a selectable frame around the message bubble
* insert a top notice with its own independent width
--> -->
<include layout="@layout/list_item_conversation_top_notice_in" />
<include layout="@layout/list_item_conversation_msg_in_content" /> <include layout="@layout/list_item_conversation_msg_in_content" />
</FrameLayout> </LinearLayout>

View File

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- This layout gets wrapped in *_msg_in.xml -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"

View File

@@ -1,10 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/list_item_background_selectable"> android:background="@drawable/list_item_background_selectable"
android:orientation="vertical"
android:paddingTop="@dimen/message_bubble_margin">
<include layout="@layout/list_item_conversation_top_notice_out" />
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout" android:id="@+id/layout"
@@ -98,4 +102,4 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout> </LinearLayout>

View File

@@ -5,8 +5,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/list_item_background_selectable" android:background="@drawable/list_item_background_selectable"
android:orientation="vertical" android:orientation="vertical">
android:paddingTop="@dimen/message_bubble_margin">
<include layout="@layout/list_item_conversation_top_notice_in" />
<com.vanniktech.emoji.EmojiTextView <com.vanniktech.emoji.EmojiTextView
android:id="@+id/msgText" android:id="@+id/msgText"
@@ -15,11 +16,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_bubble_margin_tail" android:layout_marginStart="@dimen/message_bubble_margin_tail"
android:layout_marginLeft="@dimen/message_bubble_margin_tail" android:layout_marginLeft="@dimen/message_bubble_margin_tail"
android:layout_marginTop="@dimen/message_bubble_margin"
android:layout_marginEnd="@dimen/message_bubble_margin_non_tail" android:layout_marginEnd="@dimen/message_bubble_margin_non_tail"
android:layout_marginRight="@dimen/message_bubble_margin_non_tail" android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
android:background="@drawable/msg_in_top" android:background="@drawable/msg_in_top"
android:elevation="@dimen/message_bubble_elevation" android:elevation="@dimen/message_bubble_elevation"
tools:text="Short message" /> tools:text="Short message"
tools:visibility="visible" />
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout" android:id="@+id/layout"

View File

@@ -8,6 +8,8 @@
android:orientation="vertical" android:orientation="vertical"
android:paddingTop="@dimen/message_bubble_margin"> android:paddingTop="@dimen/message_bubble_margin">
<include layout="@layout/list_item_conversation_top_notice_out" />
<com.vanniktech.emoji.EmojiTextView <com.vanniktech.emoji.EmojiTextView
android:id="@+id/msgText" android:id="@+id/msgText"
style="@style/TextMessage" style="@style/TextMessage"
@@ -15,12 +17,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_bubble_margin_non_tail" android:layout_marginStart="@dimen/message_bubble_margin_non_tail"
android:layout_marginLeft="@dimen/message_bubble_margin_non_tail" android:layout_marginLeft="@dimen/message_bubble_margin_non_tail"
android:layout_marginTop="@dimen/message_bubble_margin"
android:layout_marginEnd="@dimen/message_bubble_margin_tail" android:layout_marginEnd="@dimen/message_bubble_margin_tail"
android:layout_marginRight="@dimen/message_bubble_margin_tail" android:layout_marginRight="@dimen/message_bubble_margin_tail"
android:background="@drawable/msg_out_top" android:background="@drawable/msg_out_top"
android:elevation="@dimen/message_bubble_elevation" android:elevation="@dimen/message_bubble_elevation"
android:textColor="@color/briar_text_primary_inverse" android:textColor="@color/briar_text_primary_inverse"
tools:text="This is a long long long message that spans over several lines.\n\nIt ends here." /> tools:text="This is a long long long message that spans over several lines.\n\nIt ends here."
tools:visibility="visible" />
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout" android:id="@+id/layout"

View File

@@ -5,8 +5,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/list_item_background_selectable" android:background="@drawable/list_item_background_selectable"
android:orientation="vertical" android:orientation="vertical">
android:paddingTop="@dimen/message_bubble_margin">
<include layout="@layout/list_item_conversation_top_notice_in" />
<com.vanniktech.emoji.EmojiTextView <com.vanniktech.emoji.EmojiTextView
android:id="@+id/msgText" android:id="@+id/msgText"
@@ -15,6 +16,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_bubble_margin_tail" android:layout_marginStart="@dimen/message_bubble_margin_tail"
android:layout_marginLeft="@dimen/message_bubble_margin_tail" android:layout_marginLeft="@dimen/message_bubble_margin_tail"
android:layout_marginTop="@dimen/message_bubble_margin"
android:layout_marginEnd="@dimen/message_bubble_margin_non_tail" android:layout_marginEnd="@dimen/message_bubble_margin_non_tail"
android:layout_marginRight="@dimen/message_bubble_margin_non_tail" android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
android:background="@drawable/msg_in_top" android:background="@drawable/msg_in_top"

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<com.vanniktech.emoji.EmojiTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/topNotice"
style="@style/TextMessage.Notice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_bubble_margin_tail"
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
android:layout_marginTop="@dimen/message_bubble_margin"
android:layout_marginEnd="@dimen/message_bubble_margin_non_tail"
android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
android:layout_marginBottom="@dimen/message_bubble_margin"
android:background="@drawable/notice_in"
android:elevation="@dimen/message_bubble_elevation"
android:paddingBottom="@dimen/message_bubble_padding_top"
android:visibility="gone"
tools:text="@string/auto_delete_msg_contact_enabled"
tools:visibility="visible" />

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<com.vanniktech.emoji.EmojiTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/topNotice"
style="@style/TextMessage.Notice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|right"
android:layout_marginStart="@dimen/message_bubble_margin_non_tail"
android:layout_marginLeft="@dimen/message_bubble_margin_non_tail"
android:layout_marginEnd="@dimen/message_bubble_margin_tail"
android:layout_marginRight="@dimen/message_bubble_margin_tail"
android:layout_marginBottom="@dimen/message_bubble_margin"
android:background="@drawable/notice_out"
android:elevation="@dimen/message_bubble_elevation"
android:paddingBottom="@dimen/message_bubble_padding_top"
android:textColor="@color/private_message_date_inverse"
android:visibility="gone"
tools:showIn="@layout/list_item_conversation_msg_out"
tools:text="@string/auto_delete_msg_you_enabled"
tools:visibility="visible" />

View File

@@ -166,6 +166,14 @@
<string name="set_contact_alias">Change contact name</string> <string name="set_contact_alias">Change contact name</string>
<string name="set_contact_alias_hint">Contact name</string> <string name="set_contact_alias_hint">Contact name</string>
<string name="menu_item_auto_delete">Disappearing messages</string> <string name="menu_item_auto_delete">Disappearing messages</string>
<string name="auto_delete_msg_you_enabled">You turned on disappearing messages. Your messages will disappear after 7 days.</string>
<string name="auto_delete_msg_you_disabled">You turned off disappearing messages. Your messages will not disappear.</string>
<string name="auto_delete_msg_you_enabled_mirrored">Your messages will disappear after 7 days.</string>
<string name="auto_delete_msg_you_disabled_mirrored">Your messages will not disappear.</string>
<string name="auto_delete_msg_contact_enabled">%1$s turned on disappearing messages. %2$s\'s messages will disappear after 7 days.</string>
<string name="auto_delete_msg_contact_disabled">%1$s turned off disappearing messages. %2$s\'s messages will not disappear.</string>
<string name="auto_delete_msg_contact_enabled_mirrored">%1$s\'s messages will disappear after 7 days.</string>
<string name="auto_delete_msg_contact_disabled_mirrored">%1$s\'s messages will not disappear.</string>
<string name="delete_all_messages">Delete all messages</string> <string name="delete_all_messages">Delete all messages</string>
<string name="dialog_title_delete_all_messages">Confirm Message Deletion</string> <string name="dialog_title_delete_all_messages">Confirm Message Deletion</string>
<string name="dialog_message_delete_all_messages">Are you sure that you want to delete all messages?</string> <string name="dialog_message_delete_all_messages">Are you sure that you want to delete all messages?</string>