Detailed changes
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="?icon_tint"
+ android:pathData="M160,760L160,680L560,680L560,760L160,760ZM160,600L160,520L800,520L800,600L160,600ZM160,440L160,360L800,360L800,440L160,440ZM160,280L160,200L800,200L800,280L160,280Z"/>
+</vector>
@@ -518,7 +518,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
}
private Pair<StringBuilder, Boolean> bodyMinusFallbacks(String... fallbackNames) {
- StringBuilder body = new StringBuilder(this.body);
+ StringBuilder body = new StringBuilder(this.body == null ? "" : this.body);
List<Element> fallbacks = getFallbacks(fallbackNames);
List<Pair<Integer, Integer>> spans = new ArrayList<>();
@@ -541,6 +541,8 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
}
public String getBody() {
+ if (body == null) return "";
+
Pair<StringBuilder, Boolean> result = bodyMinusFallbacks("http://jabber.org/protocol/address", Namespace.OOB);
StringBuilder body = result.first;
@@ -568,8 +570,8 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
}
public synchronized void setBody(Spanned span) {
- setBody(span.toString());
- if (SpannedToXHTML.isPlainText(span)) {
+ setBody(span == null ? null : span.toString());
+ if (span == null || SpannedToXHTML.isPlainText(span)) {
this.payloads.remove(getHtml(true));
} else {
final Element body = getOrMakeHtml();
@@ -64,6 +64,7 @@ public class MessageGenerator extends AbstractGenerator {
packet.addChild("replace", "urn:xmpp:message-correct:0").setAttribute("id", message.getEditedIdWireFormat());
}
if (!legacyEncryption) {
+ if (message.getSubject() != null && message.getSubject().length() > 0) packet.addChild("subject").setContent(message.getSubject());
// Legacy encryption can't handle advanced payloads
for (Element el : message.getPayloads()) {
packet.addChild(el);
@@ -658,12 +658,13 @@ public class XmppConnectionService extends Service {
return this.mAvatarService;
}
- public void attachLocationToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
+ public void attachLocationToConversation(final Conversation conversation, final Uri uri, final String subject, final UiCallback<Message> callback) {
int encryption = conversation.getNextEncryption();
if (encryption == Message.ENCRYPTION_PGP) {
encryption = Message.ENCRYPTION_DECRYPTED;
}
Message message = new Message(conversation, uri.toString(), encryption);
+ if (subject != null && subject.length() > 0) message.setSubject(subject);
message.setThread(conversation.getThread());
Message.configurePrivateMessage(message);
if (encryption == Message.ENCRYPTION_DECRYPTED) {
@@ -674,7 +675,7 @@ public class XmppConnectionService extends Service {
}
}
- public void attachFileToConversation(final Conversation conversation, final Uri uri, final String type, final UiCallback<Message> callback) {
+ public void attachFileToConversation(final Conversation conversation, final Uri uri, final String type, final String subject, final UiCallback<Message> callback) {
final Message message;
if (conversation.getReplyTo() == null) {
message = new Message(conversation, "", conversation.getNextEncryption());
@@ -685,6 +686,8 @@ public class XmppConnectionService extends Service {
if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
}
+ if (subject.length() > 0) message.setSubject(subject);
+ if (subject != null && subject.length() > 0) message.setSubject(subject);
message.setThread(conversation.getThread());
if (!Message.configurePrivateFileMessage(message)) {
message.setCounterpart(conversation.getNextCounterpart());
@@ -700,7 +703,7 @@ public class XmppConnectionService extends Service {
}
}
- public void attachImageToConversation(final Conversation conversation, final Uri uri, final String type, final UiCallback<Message> callback) {
+ public void attachImageToConversation(final Conversation conversation, final Uri uri, final String type, final String subject, final UiCallback<Message> callback) {
final String mimeType = MimeUtils.guessMimeTypeFromUriAndMime(this, uri, type);
final String compressPictures = getCompressPicturesPreference();
@@ -709,7 +712,7 @@ public class XmppConnectionService extends Service {
|| (mimeType != null && mimeType.endsWith("/gif"))
|| getFileBackend().unusualBounds(uri)) {
Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": not compressing picture. sending as file");
- attachFileToConversation(conversation, uri, mimeType, callback);
+ attachFileToConversation(conversation, uri, mimeType, subject, callback);
return;
}
final Message message;
@@ -723,6 +726,7 @@ public class XmppConnectionService extends Service {
if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
}
+ if (subject != null && subject.length() > 0) message.setSubject(subject);
message.setThread(conversation.getThread());
if (!Message.configurePrivateFileMessage(message)) {
message.setCounterpart(conversation.getNextCounterpart());
@@ -734,7 +738,7 @@ public class XmppConnectionService extends Service {
getFileBackend().copyImageToPrivateStorage(message, uri);
} catch (FileBackend.ImageCompressionException e) {
Log.d(Config.LOGTAG, "unable to compress image. fall back to file transfer", e);
- attachFileToConversation(conversation, uri, mimeType, callback);
+ attachFileToConversation(conversation, uri, mimeType, subject, callback);
return;
} catch (final FileBackend.FileCopyException e) {
callback.error(e.getResId(), message);
@@ -618,6 +618,8 @@ public class ConversationFragment extends XmppFragment
} else {
binding.textinput.setText("");
}
+ binding.textinputSubject.setText("");
+ binding.textinputSubject.setVisibility(View.GONE);
updateChatMsgHint();
updateSendButton();
updateEditablity();
@@ -817,13 +819,17 @@ public class ConversationFragment extends XmppFragment
if (conversation == null) {
return;
}
+ final String subject = binding.textinputSubject.getText().toString();
activity.xmppConnectionService.attachLocationToConversation(
conversation,
uri,
+ subject,
new UiCallback<Message>() {
@Override
- public void success(Message message) {}
+ public void success(Message message) {
+ messageSent();
+ }
@Override
public void error(int errorCode, Message object) {
@@ -839,6 +845,7 @@ public class ConversationFragment extends XmppFragment
if (conversation == null) {
return;
}
+ final String subject = binding.textinputSubject.getText().toString();
if (type == "application/xdc+zip") newSubThread();
final Toast prepareFileToast =
Toast.makeText(getActivity(), getText(R.string.preparing_file), Toast.LENGTH_LONG);
@@ -848,6 +855,7 @@ public class ConversationFragment extends XmppFragment
conversation,
uri,
type,
+ subject,
new UiInformableCallback<Message>() {
@Override
public void inform(final String text) {
@@ -859,7 +867,7 @@ public class ConversationFragment extends XmppFragment
public void success(Message message) {
runOnUiThread(() -> {
activity.hideToast();
- setupReply(null);
+ messageSent();
});
hidePrepareFileToast(prepareFileToast);
}
@@ -887,6 +895,7 @@ public class ConversationFragment extends XmppFragment
if (conversation == null) {
return;
}
+ final String subject = binding.textinputSubject.getText().toString();
final Toast prepareFileToast =
Toast.makeText(getActivity(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
prepareFileToast.show();
@@ -895,6 +904,7 @@ public class ConversationFragment extends XmppFragment
conversation,
uri,
type,
+ subject,
new UiCallback<Message>() {
@Override
@@ -905,7 +915,7 @@ public class ConversationFragment extends XmppFragment
@Override
public void success(Message message) {
hidePrepareFileToast(prepareFileToast);
- runOnUiThread(() -> setupReply(null));
+ runOnUiThread(() -> messageSent());
}
@Override
@@ -934,7 +944,8 @@ public class ConversationFragment extends XmppFragment
Editable body = this.binding.textinput.getText();
if (body == null) body = new SpannableStringBuilder("");
final Conversation conversation = this.conversation;
- if (body.length() == 0 || conversation == null) {
+ final boolean hasSubject = binding.textinputSubject.getText().length() > 0;
+ if (conversation == null || body.length() == 0) { // (conversation.getThread() == null || !hasSubject))) https://issues.prosody.im/1838
binding.textSendButton.showContextMenu(0, 0);
return;
}
@@ -959,7 +970,7 @@ public class ConversationFragment extends XmppFragment
message.setEncryption(conversation.getNextEncryption());
} else {
message = new Message(conversation, body.toString(), conversation.getNextEncryption());
- message.setBody(body);
+ message.setBody(hasSubject && body.length() == 0 ? null : body);
if (message.bodyIsOnlyEmojis()) {
SpannableStringBuilder spannable = message.getSpannableBody(null, null);
ImageSpan[] imageSpans = spannable.getSpans(0, spannable.length(), ImageSpan.class);
@@ -981,6 +992,7 @@ public class ConversationFragment extends XmppFragment
}
}
}
+ if (hasSubject) message.setSubject(binding.textinputSubject.getText().toString());
message.setThread(conversation.getThread());
if (attention) {
message.addPayload(new Element("attention", "urn:xmpp:attention:0"));
@@ -988,7 +1000,8 @@ public class ConversationFragment extends XmppFragment
Message.configurePrivateMessage(message);
} else {
message = conversation.getCorrectingMessage();
- message.setBody(body);
+ message.setBody(hasSubject && body.length() == 0 ? null : body);
+ if (hasSubject) message.setSubject(binding.textinputSubject.getText().toString());
message.setThread(conversation.getThread());
message.putEdited(message.getUuid(), message.getServerMsgId());
message.setServerMsgId(null);
@@ -1809,6 +1822,7 @@ public class ConversationFragment extends XmppFragment
}
}
message.setBody(" ");
+ message.setSubject(null);
message.putEdited(message.getUuid(), message.getServerMsgId());
message.setServerMsgId(null);
message.setUuid(UUID.randomUUID().toString());
@@ -1923,6 +1937,9 @@ public class ConversationFragment extends XmppFragment
case R.id.attach_location:
handleAttachmentSelection(item);
break;
+ case R.id.attach_subject:
+ binding.textinputSubject.setVisibility(binding.textinputSubject.getVisibility() == View.GONE ? View.VISIBLE : View.GONE);
+ break;
case R.id.action_search:
startSearch();
break;
@@ -2856,6 +2873,10 @@ public class ConversationFragment extends XmppFragment
this.conversation.setDraftMessage(editable.toString());
this.binding.textinput.setText("");
this.binding.textinput.append(message.getBody());
+ if (message.getSubject() != null && message.getSubject().length() > 0) {
+ this.binding.textinputSubject.setText(message.getSubject());
+ this.binding.textinputSubject.setVisibility(View.VISIBLE);
+ }
}
private void highlightInConference(String nick) {
@@ -3072,6 +3093,7 @@ public class ConversationFragment extends XmppFragment
this.binding.textSendButton.setContentDescription(
activity.getString(R.string.send_message_to_x, conversation.getName()));
this.binding.textinput.setKeyboardListener(null);
+ this.binding.textinputSubject.setKeyboardListener(null);
final boolean participating =
conversation.getMode() == Conversational.MODE_SINGLE
|| conversation.getMucOptions().participating();
@@ -3082,6 +3104,7 @@ public class ConversationFragment extends XmppFragment
this.binding.textinput.setText(MessageUtils.EMPTY_STRING);
}
this.binding.textinput.setKeyboardListener(this);
+ this.binding.textinputSubject.setKeyboardListener(this);
messageListAdapter.updatePreferences();
refresh(false);
activity.invalidateOptionsMenu();
@@ -3556,6 +3579,8 @@ public class ConversationFragment extends XmppFragment
}
protected void messageSent() {
+ binding.textinputSubject.setText("");
+ binding.textinputSubject.setVisibility(View.GONE);
setThread(null);
conversation.setUserSelectedThread(false);
mSendingPgpMessage.set(false);
@@ -3636,7 +3661,7 @@ public class ConversationFragment extends XmppFragment
if (hasAttachments) {
action = SendButtonAction.TEXT;
} else {
- action = SendButtonTool.getAction(getActivity(), c, text);
+ action = SendButtonTool.getAction(getActivity(), c, text, binding.textinputSubject.getText().toString());
}
if (c.getAccount().getStatus() == Account.State.ONLINE) {
if (activity != null
@@ -3658,7 +3683,7 @@ public class ConversationFragment extends XmppFragment
final Activity activity = getActivity();
if (activity != null) {
this.binding.textSendButton.setImageDrawable(
- SendButtonTool.getSendButtonImageResource(activity, action, status, text.length() > 0 || hasAttachments));
+ SendButtonTool.getSendButtonImageResource(activity, action, status, text.length() > 0 || hasAttachments)); // || (c.getThread() != null && binding.textinputSubject.getText().length() > 0))); https://issues.prosody.im/1838
}
ViewGroup.LayoutParams params = binding.threadIdenticonLayout.getLayoutParams();
@@ -44,14 +44,14 @@ import eu.siacs.conversations.utils.UIHelper;
public class SendButtonTool {
- public static SendButtonAction getAction(final Activity activity, final Conversation c, final String text) {
+ public static SendButtonAction getAction(final Activity activity, final Conversation c, final String text, final String subject) {
if (activity == null) {
return SendButtonAction.TEXT;
}
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
final boolean empty = text.length() == 0;
final boolean conference = c.getMode() == Conversation.MODE_MULTI;
- if (c.getCorrectingMessage() != null && (empty || (text.equals(c.getCorrectingMessage().getBody()) && (c.getThread() == c.getCorrectingMessage().getThread() || (c.getThread() != null && c.getThread().equals(c.getCorrectingMessage().getThread())))))) {
+ if (c.getCorrectingMessage() != null && (empty || (text.equals(c.getCorrectingMessage().getBody()) && (subject.equals(c.getCorrectingMessage().getSubject())) && (c.getThread() == c.getCorrectingMessage().getThread() || (c.getThread() != null && c.getThread().equals(c.getCorrectingMessage().getThread())))))) {
return SendButtonAction.CANCEL;
} else if (conference && !c.getAccount().httpUploadAvailable()) {
if (empty && c.getNextCounterpart() != null) {
@@ -60,7 +60,7 @@ public class SendButtonTool {
return SendButtonAction.TEXT;
}
} else {
- if (empty) {
+ if (empty && (c.getThread() == null || subject.length() == 0)) {
if (conference && c.getNextCounterpart() != null) {
return SendButtonAction.CANCEL;
} else {
@@ -105,17 +105,6 @@
android:layout_toEndOf="@+id/thread_identicon_layout"
android:layout_toRightOf="@+id/thread_identicon_layout"
android:orientation="vertical">
-
- <TextView
- android:id="@+id/text_input_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:maxLines="1"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="@style/TextAppearance.Conversations.Caption.Highlight"
- android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/media_preview"
@@ -130,6 +119,29 @@
</androidx.recyclerview.widget.RecyclerView>
+ <eu.siacs.conversations.ui.widget.EditMessage
+ android:id="@+id/textinput_subject"
+ style="@style/Widget.Conversations.EditText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="Subject"
+ android:maxLines="1"
+ android:padding="8dp"
+ android:imeOptions="flagNoExtractUi"
+ android:inputType="textShortMessage|textMultiLine|textCapSentences"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/text_input_hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:maxLines="1"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="@style/TextAppearance.Conversations.Caption.Highlight"
+ android:visibility="gone" />
+
<eu.siacs.conversations.ui.widget.EditMessage
android:id="@+id/textinput"
style="@style/Widget.Conversations.EditText"
@@ -61,6 +61,11 @@
android:id="@+id/attach_location"
android:icon="?attr/ic_attach_location"
android:title="@string/send_location" />
+
+ <item
+ android:id="@+id/attach_subject"
+ android:icon="@drawable/subject"
+ android:title="Add Subject" />
</menu>
</item>
<item