indicates received messages with a tick.

Michael created

Change summary

art/ic_received_indicator.svg                                  | 76 ++++
art/render.rb                                                  |  2 
res/drawable-hdpi/ic_received_indicator.png                    |  0 
res/drawable-mdpi/ic_received_indicator.png                    |  0 
res/drawable-xhdpi/ic_received_indicator.png                   |  0 
res/drawable-xxhdpi/ic_received_indicator.png                  |  0 
res/layout/message_sent.xml                                    | 12 
res/values-de/strings.xml                                      |  5 
res/values/strings.xml                                         |  3 
res/xml/preferences.xml                                        |  9 
src/eu/siacs/conversations/entities/Message.java               | 12 
src/eu/siacs/conversations/generator/MessageGenerator.java     |  3 
src/eu/siacs/conversations/parser/MessageParser.java           |  7 
src/eu/siacs/conversations/services/XmppConnectionService.java |  4 
src/eu/siacs/conversations/ui/ConversationActivity.java        |  4 
src/eu/siacs/conversations/ui/adapter/MessageAdapter.java      | 16 
16 files changed, 143 insertions(+), 10 deletions(-)

Detailed changes

art/ic_received_indicator.svg 🔗

@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="95"
+   height="95"
+   id="Yes_check"
+   inkscape:version="0.48.5 r10040"
+   sodipodi:docname="ic_received_indicator.svg">
+  <metadata
+     id="metadata10">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1233"
+     inkscape:window-height="828"
+     id="namedview8"
+     showgrid="false"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="5.04"
+     inkscape:cx="26.829268"
+     inkscape:cy="37.489149"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="Yes_check"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <defs
+     id="defs1373">
+    <linearGradient
+       id="linearGradient2250">
+      <stop
+         style="stop-color:#008700;stop-opacity:1"
+         offset="0"
+         id="stop2252" />
+      <stop
+         style="stop-color:#006f00;stop-opacity:1"
+         offset="1"
+         id="stop2254" />
+    </linearGradient>
+  </defs>
+  <path
+     d="m 2.3894499,61.412131 c 0,0 16.7473651,20.271938 22.3528491,26.154483 3.648598,3.026816 12.878061,3.83429 14.880462,0 1.64903,-2.636163 2.380404,-5.8348 2.991819,-7.931771 C 49.920898,54.575958 72.297563,22.337321 92.321082,10.50894 96.814837,5.2377522 86.327596,3.5063483 77.217442,6.9958109 63.487006,12.254946 34.107717,59.529917 29.270873,69.192545 22.40265,70.841418 12.518762,52.447046 12.518762,52.447046 7.3805037,52.552428 1.8841059,52.071763 2.3894499,61.412131 z"
+     style="fill:#249b25;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.29981154;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+     id="check"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="cccscsccc" />
+</svg>

art/render.rb 🔗

@@ -1,6 +1,6 @@
 #!/bin/env ruby
 resolutions={'mdpi'=> 1, 'hdpi' => 1.5, 'xhdpi' => 2, 'xxhdpi' => 3}
-images = { 'conversations.svg' => ['ic_launcher',48], 'conversations_baloon.svg' => ['ic_activity', 32], 'conversations_mono.svg' => ['ic_notification',24] }
+images = { 'conversations.svg' => ['ic_launcher',48], 'conversations_baloon.svg' => ['ic_activity', 32], 'conversations_mono.svg' => ['ic_notification',24], 'ic_received_indicator.svg' => ['ic_received_indicator',12] }
 images.each do |source, result|
 	resolutions.each do |name, factor|
 		size = factor * result[1]

res/layout/message_sent.xml 🔗

@@ -63,6 +63,16 @@
                     android:textColor="@color/secondarytext"
                     android:textSize="?attr/TextSizeInfo" />
 
+                <ImageView
+                    android:id="@+id/indicator_received"
+                    android:layout_width="?attr/TextSizeInfo"
+                    android:layout_height="?attr/TextSizeInfo"
+                    android:layout_gravity="center_vertical"
+                    android:layout_marginLeft="6sp"
+                    android:layout_marginTop="2sp"
+                    android:gravity="center_vertical"
+                    android:src="@drawable/ic_received_indicator" />
+
                 <ImageView
                     android:id="@+id/security_indicator"
                     android:layout_width="?attr/TextSizeInfo"
@@ -87,4 +97,4 @@
         android:scaleType="fitXY"
         android:src="@drawable/ic_profile" />
 
-</RelativeLayout>
+</RelativeLayout>

res/values-de/strings.xml 🔗

@@ -232,6 +232,8 @@
     <string name="additional_information">Zusätzliche Informationen</string>
     <string name="skip">Überspringen</string>
     <string name="pref_ui_options">Benutzeroberfläche</string>
+    <string name="pref_use_indicate_received">Anfrage für Nachrichten Empfang</string>
+    <string name="pref_use_indicate_received_summary">Wenn Nachricht vom Empfänger empfangen wurde dann erscheint ein Häckchen als Bestätigung (muss vom Client des Empfängers unterstützt werden und funktioniert nicht in jedem Fall)</string>
     <string name="disable_notifications">Benachrichtigungen deaktivieren</string>
     <string name="disable_notifications_for_this_conversation">Benachrichtigungen für diese Unterhaltung deaktivieren</string>
     <string name="notifications_disabled">Benachrichtigungen sind deaktiviert</string>
@@ -256,5 +258,6 @@
     <string name="pref_use_larger_font_summary">Überall in der App eine größere Schrift verwenden</string>
     <string name="pref_use_send_button_to_indicate_status">Absende-Knopf zeigt Online-Status an</string>
     <string name="pref_use_send_button_to_indicate_status_summary">Absende-Knopf einfärben, um den Online-Status des Kontakts zu signalisieren</string>
+    <string name="pref_expert_options_other">Anderes - nicht kategorisiert</string>
 
-</resources>
+</resources>

res/values/strings.xml 🔗

@@ -255,6 +255,9 @@
     <string name="pref_use_larger_font">Increase font size</string>
     <string name="pref_use_larger_font_summary">Use larger font sizes across the entire app</string>
     <string name="pref_use_send_button_to_indicate_status">Send button indicates status</string>
+    <string name="pref_use_indicate_received">Request message receipts</string>
+    <string name="pref_use_indicate_received_summary">When message has been received by the receiver then a tick will appear as confirmation (must supported by the recipient client and does not work as any case)</string>
     <string name="pref_use_send_button_to_indicate_status_summary">Colorize send button to indicate contact status</string>
+    <string name="pref_expert_options_other">Other - not categorized</string>
 
 </resources>

res/xml/preferences.xml 🔗

@@ -90,6 +90,13 @@
                     android:summary="@string/pref_dont_save_encrypted_summary"
                     android:title="@string/pref_dont_save_encrypted" />
             </PreferenceCategory>
+                <PreferenceCategory android:title="@string/pref_expert_options_other" >
+                    <CheckBoxPreference
+                        android:defaultValue="false"
+                        android:key="indicate_received"
+                        android:summary="@string/pref_use_indicate_received_summary"
+                        android:title="@string/pref_use_indicate_received" />
+                </PreferenceCategory>
         </PreferenceScreen>
 
         <CheckBoxPreference
@@ -99,4 +106,4 @@
             android:title="@string/pref_never_send_crash" />
     </PreferenceCategory>
 
-</PreferenceScreen>
+</PreferenceScreen>

src/eu/siacs/conversations/entities/Message.java 🔗

@@ -326,12 +326,12 @@ public class Message extends AbstractEntity {
 				&& this.getType() == message.getType()
 				&& this.getEncryption() == message.getEncryption()
 				&& this.getCounterpart().equals(message.getCounterpart())
-				&& (message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) && ((this
-				.getStatus() == message.getStatus()) || ((this.getStatus() == Message.STATUS_SEND || this
-				.getStatus() == Message.STATUS_SEND_RECEIVED) && (message
-				.getStatus() == Message.STATUS_UNSEND
-				|| message.getStatus() == Message.STATUS_SEND || message
-					.getStatus() == Message.STATUS_SEND_DISPLAYED))));
+				&& (message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000)
+				&& ((this.getStatus() == message.getStatus())
+				|| (this.getStatus() == Message.STATUS_SEND && (message.getStatus() == Message.STATUS_UNSEND
+				|| message.getStatus() == Message.STATUS_SEND))
+				|| (this.getStatus() == Message.STATUS_SEND_RECEIVED
+				&& message.getStatus() == Message.STATUS_SEND_DISPLAYED)));
 	}
 
 	public String getMergedBody() {

src/eu/siacs/conversations/generator/MessageGenerator.java 🔗

@@ -27,6 +27,9 @@ public class MessageGenerator extends AbstractGenerator {
 			packet.setTo(message.getCounterpart());
 			packet.setType(MessagePacket.TYPE_CHAT);
 			packet.addChild("markable", "urn:xmpp:chat-markers:0");
+			if (this.mXmppConnectionService.indicateReceived()) {
+				packet.addChild("request", "urn:xmpp:receipts");
+			}
 		} else if (message.getType() == Message.TYPE_PRIVATE) {
 			packet.setTo(message.getCounterpart());
 			packet.setType(MessagePacket.TYPE_CHAT);

src/eu/siacs/conversations/parser/MessageParser.java 🔗

@@ -278,6 +278,13 @@ public class MessageParser extends AbstractParser implements
 			updateLastseen(packet, account, false);
 			mXmppConnectionService.markMessage(account, fromParts[0], id,
 					Message.STATUS_SEND_RECEIVED);
+		} else if (packet.hasChild("received", "urn:xmpp:receipts")) {
+			String id = packet.findChild("received", "urn:xmpp:receipts")
+					.getAttribute("id");
+			String[] fromParts = packet.getAttribute("from").split("/");
+			updateLastseen(packet, account, false);
+			mXmppConnectionService.markMessage(account, fromParts[0], id,
+					Message.STATUS_SEND_RECEIVED);
 		} else if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
 			Element x = packet.findChild("x",
 					"http://jabber.org/protocol/muc#user");

src/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -1566,6 +1566,10 @@ public class XmppConnectionService extends Service {
 		return !getPreferences().getBoolean("dont_save_encrypted", false);
 	}
 
+	public boolean indicateReceived() {
+		return getPreferences().getBoolean("indicate_received", false);
+	}
+
 	public void notifyUi(Conversation conversation, boolean notify) {
 		if (mOnConversationUpdate != null) {
 			mOnConversationUpdate.onConversationUpdate();

src/eu/siacs/conversations/ui/ConversationActivity.java 🔗

@@ -790,6 +790,10 @@ public class ConversationActivity extends XmppActivity implements
 		return getPreferences().getBoolean("send_button_status", false);
 	}
 
+	public boolean indicateReceived() {
+		return getPreferences().getBoolean("indicate_received", false);
+	}
+
 	@Override
 	public void onAccountUpdate() {
 		final ConversationFragment fragment = (ConversationFragment) getFragmentManager()

src/eu/siacs/conversations/ui/adapter/MessageAdapter.java 🔗

@@ -96,6 +96,9 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		String filesize = null;
 		String info = null;
 		boolean error = false;
+		if (viewHolder.indicatorReceived != null) {
+			viewHolder.indicatorReceived.setVisibility(View.GONE);
+		}
 		boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
 				&& message.getMergedStatus() <= Message.STATUS_RECEIVED;
 		if (message.getType() == Message.TYPE_IMAGE) {
@@ -117,6 +120,16 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		case Message.STATUS_OFFERED:
 			info = getContext().getString(R.string.offering);
 			break;
+		case Message.STATUS_SEND_RECEIVED:
+			if (activity.indicateReceived()) {
+				viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
+			}
+			break;
+		case Message.STATUS_SEND_DISPLAYED:
+			if (activity.indicateReceived()) {
+				viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
+			}
+			break;
 		case Message.STATUS_SEND_FAILED:
 			info = getContext().getString(R.string.send_failed);
 			error = true;
@@ -337,6 +350,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 						.findViewById(R.id.message_body);
 				viewHolder.time = (TextView) view
 						.findViewById(R.id.message_time);
+				viewHolder.indicatorReceived = (ImageView) view
+						.findViewById(R.id.indicator_received);
 				view.setTag(viewHolder);
 				break;
 			case RECEIVED:
@@ -512,6 +527,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		protected Button download_button;
 		protected ImageView image;
 		protected ImageView indicator;
+		protected ImageView indicatorReceived;
 		protected TextView time;
 		protected TextView messageBody;
 		protected ImageView contact_picture;