Detailed changes
@@ -98,6 +98,7 @@ dependencies {
implementation 'com.github.ipld:java-cid:v1.3.1'
implementation 'com.splitwise:tokenautocomplete:3.0.2'
implementation 'me.saket:better-link-movement-method:2.2.0'
+ implementation 'com.github.singpolyma:android-identicons:master-SNAPSHOT'
implementation 'org.snikket:webrtc-android:107.0.0'
// INSERT
}
@@ -25,6 +25,7 @@
<string name="action_execute">Go</string>
<string name="pref_theme_oledblack">OLED Black</string>
<string name="invite_to_app">Invite to Chat</string>
+ <string name="only_this_thread">Show only this thread</string>
<string name="pref_dialler_integration_incoming">Use Phone Accounts for Incoming Calls</string>
<string name="pref_dialler_integration_incoming_summary">Incoming calls from phone numbers may ring with your system dialler instead of this app\'s notification settings</string>
</resources>
@@ -121,6 +121,7 @@
<item name="icon_help" type="reference">@drawable/ic_help_white_24dp</item>
<item name="icon_goto_chat" type="reference">@drawable/ic_question_answer_white_24dp</item>
<item name="icon_secure" type="reference">@drawable/ic_lock_open_white_24dp</item>
+ <item name="icon_small_lock" type="reference">@drawable/ic_lock_black_18dp</item>
<item name="icon_settings" type="reference">@drawable/ic_settings_black_24dp</item>
<item name="icon_share" type="reference">@drawable/ic_share_white_24dp</item>
<item name="ic_cloud_download" type="reference">@drawable/ic_cloud_download_white_24dp
@@ -275,6 +276,7 @@
<item name="icon_help" type="reference">@drawable/ic_help_white_24dp</item>
<item name="icon_goto_chat" type="reference">@drawable/ic_question_answer_white_24dp</item>
<item name="icon_secure" type="reference">@drawable/ic_lock_open_white_24dp</item>
+ <item name="icon_small_lock" type="reference">@drawable/ic_lock_white_18dp</item>
<item name="icon_settings" type="reference">@drawable/ic_settings_white_24dp</item>
<item name="icon_share" type="reference">@drawable/ic_share_white_24dp</item>
<item name="ic_cloud_download" type="reference">@drawable/ic_cloud_download_white_24dp
@@ -148,6 +148,9 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
private String mFirstMamReference = null;
protected int mCurrentTab = -1;
protected ConversationPagerAdapter pagerAdapter = new ConversationPagerAdapter();
+ protected Element thread = null;
+ protected boolean lockThread = false;
+ protected boolean userSelectedThread = false;
public Conversation(final String name, final Account account, final Jid contactJid,
final int mode) {
@@ -529,7 +532,8 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
messages.addAll(this.messages);
}
for (Iterator<Message> iterator = messages.iterator(); iterator.hasNext(); ) {
- if (iterator.next().wasMergedIntoPrevious()) {
+ Message m = iterator.next();
+ if (m.wasMergedIntoPrevious() || (getLockThread() && (m.getThread() == null || !m.getThread().getContent().equals(getThread().getContent())))) {
iterator.remove();
}
}
@@ -628,6 +632,31 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
this.draftMessage = draftMessage;
}
+ public Element getThread() {
+ return this.thread;
+ }
+
+ public void setThread(Element thread) {
+ this.thread = thread;
+ }
+
+ public void setLockThread(boolean flag) {
+ this.lockThread = flag;
+ if (flag) setUserSelectedThread(true);
+ }
+
+ public boolean getLockThread() {
+ return this.lockThread;
+ }
+
+ public void setUserSelectedThread(boolean flag) {
+ this.userSelectedThread = flag;
+ }
+
+ public boolean getUserSelectedThread() {
+ return this.userSelectedThread;
+ }
+
public boolean isRead() {
synchronized (this.messages) {
for(final Message message : Lists.reverse(this.messages)) {
@@ -418,6 +418,23 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
this.subject = subject;
}
+ public Element getThread() {
+ if (this.payloads == null) return null;
+
+ for (Element el : this.payloads) {
+ if (el.getName().equals("thread") && el.getNamespace().equals("jabber:client")) {
+ return el;
+ }
+ }
+
+ return null;
+ }
+
+ public void setThread(Element thread) {
+ payloads.removeIf(el -> el.getName().equals("thread") && el.getNamespace().equals("jabber:client"));
+ addPayload(thread);
+ }
+
public void setMucUser(MucOptions.User user) {
this.user = new WeakReference<>(user);
}
@@ -907,9 +924,15 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
}
public void addPayload(Element el) {
+ if (el == null) return;
+
this.payloads.add(el);
}
+ public List<Element> getPayloads() {
+ return new ArrayList<>(this.payloads);
+ }
+
public Element getHtml() {
if (this.payloads == null) return null;
@@ -920,7 +943,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
}
return null;
- }
+ }
public List<Element> getCommands() {
if (this.payloads == null) return null;
@@ -62,6 +62,9 @@ public class MessageGenerator extends AbstractGenerator {
if (message.edited()) {
packet.addChild("replace", "urn:xmpp:message-correct:0").setAttribute("id", message.getEditedIdWireFormat());
}
+ for (Element el : message.getPayloads()) {
+ packet.addChild(el);
+ }
return packet;
}
@@ -610,6 +610,10 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if (el.getName().equals("query") && el.getNamespace().equals("http://jabber.org/protocol/disco#items") && el.getAttribute("node").equals("http://jabber.org/protocol/commands")) {
message.addPayload(el);
}
+ if (el.getName().equals("thread") && (el.getNamespace() == null || el.getNamespace().equals("jabber:client"))) {
+ el.setAttribute("xmlns", "jabber:client");
+ message.addPayload(el);
+ }
}
if (conversationMultiMode) {
message.setMucUser(conversation.getMucOptions().findUserByFullJid(counterpart));
@@ -568,6 +568,7 @@ public class XmppConnectionService extends Service {
encryption = Message.ENCRYPTION_DECRYPTED;
}
Message message = new Message(conversation, uri.toString(), encryption);
+ message.setThread(conversation.getThread());
Message.configurePrivateMessage(message);
if (encryption == Message.ENCRYPTION_DECRYPTED) {
getPgpEngine().encrypt(message, callback);
@@ -584,6 +585,7 @@ public class XmppConnectionService extends Service {
} else {
message = new Message(conversation, "", conversation.getNextEncryption());
}
+ message.setThread(conversation.getThread());
if (!Message.configurePrivateFileMessage(message)) {
message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_FILE);
@@ -616,6 +618,7 @@ public class XmppConnectionService extends Service {
} else {
message = new Message(conversation, "", conversation.getNextEncryption());
}
+ message.setThread(conversation.getThread());
if (!Message.configurePrivateFileMessage(message)) {
message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_IMAGE);
@@ -980,6 +983,7 @@ public class XmppConnectionService extends Service {
private void directReply(final Conversation conversation, final String body, final String lastMessageUuid, final boolean dismissAfterReply) {
final Message inReplyTo = lastMessageUuid == null ? null : conversation.findMessageWithUuid(lastMessageUuid);
final Message message = new Message(conversation, body, conversation.getNextEncryption());
+ if (inReplyTo != null) message.setThread(inReplyTo.getThread());
if (inReplyTo != null && inReplyTo.isPrivateMessage()) {
Message.configurePrivateMessage(message, inReplyTo.getCounterpart());
}
@@ -264,6 +264,7 @@ public class ConversationFragment extends XmppFragment
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (AbsListView.OnScrollListener.SCROLL_STATE_IDLE == scrollState) {
+ updateThreadFromLastMessage();
fireReadEvent();
}
}
@@ -854,6 +855,7 @@ public class ConversationFragment extends XmppFragment
}
private void sendMessage() {
+ conversation.setUserSelectedThread(false);
if (mediaPreviewAdapter.hasAttachments()) {
commitAttachments();
return;
@@ -870,6 +872,7 @@ public class ConversationFragment extends XmppFragment
final Message message;
if (conversation.getCorrectingMessage() == null) {
message = new Message(conversation, body, conversation.getNextEncryption());
+ message.setThread(conversation.getThread());
Message.configurePrivateMessage(message);
} else {
message = conversation.getCorrectingMessage();
@@ -953,6 +956,8 @@ public class ConversationFragment extends XmppFragment
this.binding.textinput.setHint(UIHelper.getMessageHint(getActivity(), conversation));
getActivity().invalidateOptionsMenu();
}
+
+ binding.messagesView.post(this::updateThreadFromLastMessage);
}
public void setupIme() {
@@ -1248,6 +1253,33 @@ public class ConversationFragment extends XmppFragment
new EditMessageActionModeCallback(this.binding.textinput));
}
+ messageListAdapter.setOnMessageBoxClicked(message -> {
+ setThread(message.getThread());
+ conversation.setUserSelectedThread(true);
+ });
+
+ binding.threadIdenticonLayout.setOnClickListener(v -> {
+ boolean wasLocked = conversation.getLockThread();
+ conversation.setLockThread(false);
+ if (wasLocked) {
+ conversation.setUserSelectedThread(false);
+ refresh();
+ updateThreadFromLastMessage();
+ } else {
+ newThread();
+ conversation.setUserSelectedThread(true);
+ }
+ });
+
+ binding.threadIdenticonLayout.setOnLongClickListener(v -> {
+ boolean wasLocked = conversation.getLockThread();
+ conversation.setLockThread(false);
+ setThread(null);
+ conversation.setUserSelectedThread(true);
+ if (wasLocked) refresh();
+ return true;
+ });
+
return binding.getRoot();
}
@@ -1275,9 +1307,25 @@ public class ConversationFragment extends XmppFragment
}
private void quoteMessage(Message message) {
+ setThread(message.getThread());
+ conversation.setUserSelectedThread(true);
quoteText(MessageUtils.prepareQuote(message));
}
+ private void setThread(Element thread) {
+ this.conversation.setThread(thread);
+ binding.threadIdenticon.setAlpha(0f);
+ binding.threadIdenticonLock.setVisibility(this.conversation.getLockThread() ? View.VISIBLE : View.GONE);
+ if (thread != null) {
+ final String threadId = thread.getContent();
+ if (threadId != null) {
+ binding.threadIdenticon.setAlpha(1f);
+ binding.threadIdenticon.setColor(UIHelper.getColorForName(threadId));
+ binding.threadIdenticon.setHash(UIHelper.identiconHash(threadId));
+ }
+ }
+ }
+
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
// This should cancel any remaining click events that would otherwise trigger links
@@ -1327,6 +1375,7 @@ public class ConversationFragment extends XmppFragment
MenuItem retryDecryption = menu.findItem(R.id.retry_decryption);
MenuItem correctMessage = menu.findItem(R.id.correct_message);
MenuItem retractMessage = menu.findItem(R.id.retract_message);
+ MenuItem onlyThisThread = menu.findItem(R.id.only_this_thread);
MenuItem shareWith = menu.findItem(R.id.share_with);
MenuItem sendAgain = menu.findItem(R.id.send_again);
MenuItem copyUrl = menu.findItem(R.id.copy_url);
@@ -1334,6 +1383,7 @@ public class ConversationFragment extends XmppFragment
MenuItem cancelTransmission = menu.findItem(R.id.cancel_transmission);
MenuItem deleteFile = menu.findItem(R.id.delete_file);
MenuItem showErrorMessage = menu.findItem(R.id.show_error_message);
+ onlyThisThread.setVisible(!conversation.getLockThread() && m.getThread() != null);
final boolean unInitiatedButKnownSize = MessageUtils.unInitiatedButKnownSize(m);
final boolean showError =
m.getStatus() == Message.STATUS_SEND_FAILED
@@ -1470,6 +1520,11 @@ public class ConversationFragment extends XmppFragment
case R.id.open_with:
openWith(selectedMessage);
return true;
+ case R.id.only_this_thread:
+ conversation.setLockThread(true);
+ setThread(selectedMessage.getThread());
+ refresh();
+ return true;
default:
return super.onContextItemSelected(item);
}
@@ -2116,7 +2171,29 @@ public class ConversationFragment extends XmppFragment
}
}
+ private void newThread() {
+ Element thread = new Element("thread", "jabber:client");
+ thread.setContent(UUID.randomUUID().toString());
+ setThread(thread);
+ }
+
+ private void updateThreadFromLastMessage() {
+ if (this.conversation != null && !this.conversation.getUserSelectedThread() && TextUtils.isEmpty(binding.textinput.getText())) {
+ Message message = getLastVisibleMessage();
+ if (message == null) {
+ newThread();
+ } else {
+ setThread(message.getThread());
+ }
+ }
+ }
+
private String getLastVisibleMessageUuid() {
+ Message message = getLastVisibleMessage();
+ return message == null ? null : message.getUuid();
+ }
+
+ private Message getLastVisibleMessage() {
if (binding == null) {
return null;
}
@@ -2140,7 +2217,7 @@ public class ConversationFragment extends XmppFragment
while (message.next() != null && message.next().wasMergedIntoPrevious()) {
message = message.next();
}
- return message.getUuid();
+ return message;
}
}
}
@@ -3684,13 +3761,9 @@ public class ConversationFragment extends XmppFragment
@Override
public void onContactPictureClicked(Message message) {
- String fingerprint;
- if (message.getEncryption() == Message.ENCRYPTION_PGP
- || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- fingerprint = "pgp";
- } else {
- fingerprint = message.getFingerprint();
- }
+ setThread(message.getThread());
+ conversation.setUserSelectedThread(true);
+
final boolean received = message.getStatus() <= Message.STATUS_RECEIVED;
if (received) {
if (message.getConversation() instanceof Conversation
@@ -3724,15 +3797,8 @@ public class ConversationFragment extends XmppFragment
.show();
}
}
- return;
- } else {
- if (!message.getContact().isSelf()) {
- activity.switchToContactDetails(message.getContact(), fingerprint);
- return;
- }
}
}
- activity.switchToAccount(message.getConversation().getAccount(), fingerprint);
}
private Activity requireActivity() {
@@ -42,6 +42,8 @@ import com.cheogram.android.BobTransfer;
import com.google.common.base.Strings;
+import com.lelloman.identicon.view.GithubIdenticonView;
+
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -107,6 +109,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private List<String> highlightedTerm = null;
private final DisplayMetrics metrics;
private OnContactPictureClicked mOnContactPictureClickedListener;
+ private OnContactPictureClicked mOnMessageBoxClickedListener;
private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
private boolean mUseGreenBackground = false;
private final boolean mForceNames;
@@ -146,6 +149,10 @@ public class MessageAdapter extends ArrayAdapter<Message> {
this.mOnContactPictureClickedListener = listener;
}
+ public void setOnMessageBoxClicked(OnContactPictureClicked listener) {
+ this.mOnMessageBoxClickedListener = listener;
+ }
+
public Activity getActivity() {
return activity;
}
@@ -717,6 +724,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.subject = view.findViewById(R.id.message_subject);
viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received);
viewHolder.audioPlayer = view.findViewById(R.id.audio_player);
+ viewHolder.thread_identicon = view.findViewById(R.id.thread_identicon);
break;
case RECEIVED:
view = activity.getLayoutInflater().inflate(R.layout.message_received, parent, false);
@@ -733,6 +741,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.encryption = view.findViewById(R.id.message_encryption);
viewHolder.audioPlayer = view.findViewById(R.id.audio_player);
viewHolder.commands_list = view.findViewById(R.id.commands_list);
+ viewHolder.thread_identicon = view.findViewById(R.id.thread_identicon);
break;
case STATUS:
view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false);
@@ -751,6 +760,19 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
}
+ if (viewHolder.thread_identicon != null) {
+ viewHolder.thread_identicon.setVisibility(View.GONE);
+ final Element thread = message.getThread();
+ if (thread != null) {
+ final String threadId = thread.getContent();
+ if (threadId != null) {
+ viewHolder.thread_identicon.setVisibility(View.VISIBLE);
+ viewHolder.thread_identicon.setColor(UIHelper.getColorForName(threadId));
+ viewHolder.thread_identicon.setHash(UIHelper.identiconHash(threadId));
+ }
+ }
+ }
+
boolean darkBackground = type == RECEIVED && (!isInValidSession || mUseGreenBackground) || activity.isDarkTheme();
if (type == DATE_SEPARATOR) {
@@ -822,6 +844,18 @@ public class MessageAdapter extends ArrayAdapter<Message> {
resetClickListener(viewHolder.message_box, viewHolder.messageBody);
+ viewHolder.message_box.setOnClickListener(v -> {
+ if (MessageAdapter.this.mOnMessageBoxClickedListener != null) {
+ MessageAdapter.this.mOnMessageBoxClickedListener
+ .onContactPictureClicked(message);
+ }
+ });
+ viewHolder.messageBody.setOnClickListener(v -> {
+ if (MessageAdapter.this.mOnMessageBoxClickedListener != null) {
+ MessageAdapter.this.mOnMessageBoxClickedListener
+ .onContactPictureClicked(message);
+ }
+ });
viewHolder.contact_picture.setOnClickListener(v -> {
if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
MessageAdapter.this.mOnContactPictureClickedListener
@@ -1028,6 +1062,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
protected TextView status_message;
protected TextView encryption;
protected ListView commands_list;
+ protected GithubIdenticonView thread_identicon;
}
class ThumbnailTask extends AsyncTask<DownloadableFile, Void, Drawable[]> {
@@ -11,8 +11,10 @@ import androidx.annotation.ColorInt;
import androidx.core.content.res.ResourcesCompat;
import com.google.common.base.Strings;
+import com.google.common.primitives.Ints;
import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Calendar;
@@ -229,6 +231,16 @@ public class UIHelper {
}
}
+ public static int identiconHash(String name) {
+ try {
+ MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
+ byte[] digest = sha1.digest(name.getBytes(StandardCharsets.UTF_8));
+ return Ints.fromByteArray(digest);
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+
public static int getColorForName(String name) {
return getColorForName(name, false);
}
@@ -22,6 +22,7 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
+ android:layout_toEndOf="@+id/account_image"
android:layout_toRightOf="@+id/account_image"
android:orientation="vertical"
android:paddingLeft="@dimen/avatar_item_distance"
@@ -60,10 +60,10 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_alignParentLeft="true"
android:layout_toStartOf="@+id/textSendButton"
android:layout_toLeftOf="@+id/textSendButton"
+ android:layout_toEndOf="@+id/thread_identicon_layout"
+ android:layout_toRightOf="@+id/thread_identicon_layout"
android:orientation="vertical">
<TextView
@@ -109,6 +109,35 @@
</LinearLayout>
+ <RelativeLayout
+ android:id="@+id/thread_identicon_layout"
+ android:layout_width="30dp"
+ android:layout_height="30dp"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:layout_marginLeft="8dp">
+
+ <com.lelloman.identicon.view.GithubIdenticonView
+ android:id="@+id/thread_identicon"
+ android:alpha="0"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_centerVertical="true"
+ android:contentDescription="Thread Marker" />
+ <ImageView
+ android:id="@+id/thread_identicon_lock"
+ android:layout_width="12dp"
+ android:layout_height="12dp"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:visibility="gone"
+ android:src="?attr/icon_small_lock"
+ android:contentDescription="Thread Locked" />
+
+ </RelativeLayout>
+
<ImageButton
android:id="@+id/textSendButton"
android:layout_width="48dp"
@@ -93,6 +93,15 @@
android:gravity="center_vertical"
android:src="@drawable/ic_mode_edit_white_18dp" />
+ <com.lelloman.identicon.view.GithubIdenticonView
+ android:id="@+id/thread_identicon"
+ android:visibility="gone"
+ android:layout_width="9dp"
+ android:layout_height="9dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="4sp"
+ android:layout_marginBottom="-1dp" />
+
<TextView
android:id="@+id/message_time"
android:layout_width="wrap_content"
@@ -10,29 +10,33 @@
android:paddingRight="8dp"
android:paddingBottom="3dp">
-
- <LinearLayout
+ <RelativeLayout
android:id="@+id/message_photo_box"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
- android:layout_alignParentBottom="true"
- android:orientation="vertical">
-
- <com.makeramen.roundedimageview.RoundedImageView
- android:id="@+id/message_photo"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:scaleType="fitXY"
- app:riv_corner_radius="2dp" />
-
- <View
- android:id="@+id/placeholder"
- android:layout_width="48dp"
- android:layout_height="3dp" />
- </LinearLayout>
+ android:layout_alignParentBottom="true">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <com.makeramen.roundedimageview.RoundedImageView
+ android:id="@+id/message_photo"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="fitXY"
+ app:riv_corner_radius="2dp" />
+
+ <View
+ android:id="@+id/placeholder"
+ android:layout_width="match_parent"
+ android:layout_height="3dp" />
+ </LinearLayout>
+
+ </RelativeLayout>
<LinearLayout
android:id="@+id/message_box"
@@ -44,7 +48,6 @@
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:layout_toLeftOf="@+id/message_photo_box"
- android:translationY="-2dp"
android:elevation="3dp"
android:background="@drawable/message_bubble_sent"
android:longClickable="true"
@@ -93,6 +96,15 @@
android:text="@string/sending"
android:textAppearance="@style/TextAppearance.Conversations.Caption" />
+ <com.lelloman.identicon.view.GithubIdenticonView
+ android:id="@+id/thread_identicon"
+ android:visibility="gone"
+ android:layout_width="9dp"
+ android:layout_height="9dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="4sp"
+ android:layout_marginBottom="-1dp" />
+
<ImageView
android:id="@+id/security_indicator"
android:layout_width="?attr/TextSizeCaption"
@@ -21,6 +21,11 @@
android:title="@string/quote"
android:visible="false" />
+ <item
+ android:id="@+id/only_this_thread"
+ android:title="@string/only_this_thread"
+ android:visible="false" />
+
<item
android:id="@+id/retry_decryption"
android:title="@string/retry_decryption"
@@ -106,6 +106,7 @@
<attr name="icon_help" format="reference" />
<attr name="icon_goto_chat" format="reference" />
<attr name="icon_secure" format="reference" />
+ <attr name="icon_small_lock" format="reference" />
<attr name="icon_settings" format="reference" />
<attr name="icon_share" format="reference" />
<attr name="icon_import_export" format="reference" />