diff --git a/res/layout/activity_publish_profile_picture.xml b/res/layout/activity_publish_profile_picture.xml
index ff7f6fa2..9dd7a5dc 100644
--- a/res/layout/activity_publish_profile_picture.xml
+++ b/res/layout/activity_publish_profile_picture.xml
@@ -75,13 +75,20 @@
android:paddingLeft="8dp"
android:paddingRight="8dp" >
+
+
+ android:textSize="14sp" />
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0b999482..de925254 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -270,5 +270,5 @@
%s has read up to this point
Publish avatar
Touch avatar to select picture from gallary
- Publish avatar for %s. Everyone subscribed to your presence updates will also be able to see this picture.
+ Please note: Everyone subscribed to your presence updates will be allowed to see this picture.
diff --git a/src/eu/siacs/conversations/generator/AbstractGenerator.java b/src/eu/siacs/conversations/generator/AbstractGenerator.java
index 05d5799c..0de15092 100644
--- a/src/eu/siacs/conversations/generator/AbstractGenerator.java
+++ b/src/eu/siacs/conversations/generator/AbstractGenerator.java
@@ -18,7 +18,8 @@ public abstract class AbstractGenerator {
"http://jabber.org/protocol/muc",
"jabber:x:conference",
"http://jabber.org/protocol/caps",
- "http://jabber.org/protocol/disco#info"};
+ "http://jabber.org/protocol/disco#info",
+ "urn:xmpp:avatar:metadata+notify"};
public final String IDENTITY_NAME = "Conversations 0.5";
public final String IDENTITY_TYPE = "phone";
/*public final String[] FEATURES = { "http://jabber.org/protocol/muc","http://jabber.org/protocol/disco#info", "http://jabber.org/protocol/disco#items", "http://jabber.org/protocol/caps" };
diff --git a/src/eu/siacs/conversations/generator/IqGenerator.java b/src/eu/siacs/conversations/generator/IqGenerator.java
index 7b3350d4..1a38347b 100644
--- a/src/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/eu/siacs/conversations/generator/IqGenerator.java
@@ -5,6 +5,7 @@ import java.util.Collections;
import java.util.List;
import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class IqGenerator extends AbstractGenerator {
@@ -28,4 +29,34 @@ public class IqGenerator extends AbstractGenerator {
}
return packet;
}
+
+ protected IqPacket publish(String node, Element item) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
+ Element pubsub = packet.addChild("pubsub", "http://jabber.org/protocol/pubsub");
+ Element publish = pubsub.addChild("publish");
+ publish.setAttribute("node", node);
+ publish.addChild(item);
+ return packet;
+ }
+
+ public IqPacket publishAvatar(Avatar avatar) {
+ Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ Element data = item.addChild("data","urn:xmpp:avatar:data");
+ data.setContent(avatar.image);
+ return publish("urn:xmpp:avatar:data", item);
+ }
+
+ public IqPacket publishAvatarMetadata(Avatar avatar) {
+ Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ Element metadata = item.addChild("metadata","urn:xmpp:avatar:metadata");
+ Element info = metadata.addChild("info");
+ info.setAttribute("bytes",avatar.size);
+ info.setAttribute("id",avatar.sha1sum);
+ info.setAttribute("height",avatar.height);
+ info.setAttribute("width",avatar.height);
+ info.setAttribute("type", avatar.type);
+ return publish("urn:xmpp:avatar:metadata",item);
+ }
}
diff --git a/src/eu/siacs/conversations/parser/MessageParser.java b/src/eu/siacs/conversations/parser/MessageParser.java
index 5c04b09c..0a812669 100644
--- a/src/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/eu/siacs/conversations/parser/MessageParser.java
@@ -1,6 +1,7 @@
package eu.siacs.conversations.parser;
import android.os.SystemClock;
+import android.util.Log;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
import eu.siacs.conversations.entities.Account;
@@ -213,6 +214,10 @@ public class MessageParser extends AbstractParser implements
}
private void parseNormal(Element packet, Account account) {
+ if (packet.hasChild("event","http://jabber.org/protocol/pubsub#event")) {
+ Element event = packet.findChild("event","http://jabber.org/protocol/pubsub#event");
+ parseEvent(event,packet.getAttribute("from"),account);
+ }
if (packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
String id = packet
.findChild("displayed", "urn:xmpp:chat-markers:0")
@@ -254,6 +259,16 @@ public class MessageParser extends AbstractParser implements
}
}
+ private void parseEvent(Element event, String from, Account account) {
+ Element items = event.findChild("items");
+ String node = items.getAttribute("node");
+ if (node!=null) {
+ Log.d("xmppService",account.getJid()+": "+node+" from "+from);
+ } else {
+ Log.d("xmppService",event.toString());
+ }
+ }
+
private String getPgpBody(Element message) {
Element child = message.findChild("x", "jabber:x:encrypted");
if (child == null) {
@@ -324,6 +339,9 @@ public class MessageParser extends AbstractParser implements
} else if (packet.getType() == MessagePacket.TYPE_NORMAL) {
this.parseNormal(packet, account);
return;
+ } else if (packet.getType() == MessagePacket.TYPE_HEADLINE) {
+ this.parseHeadline(packet, account);
+ return;
}
if ((message == null) || (message.getBody() == null)) {
return;
@@ -346,4 +364,11 @@ public class MessageParser extends AbstractParser implements
}
mXmppConnectionService.notifyUi(conversation, notify);
}
+
+ private void parseHeadline(MessagePacket packet, Account account) {
+ if (packet.hasChild("event","http://jabber.org/protocol/pubsub#event")) {
+ Element event = packet.findChild("event","http://jabber.org/protocol/pubsub#event");
+ parseEvent(event,packet.getFrom(),account);
+ }
+ }
}
diff --git a/src/eu/siacs/conversations/persistance/FileBackend.java b/src/eu/siacs/conversations/persistance/FileBackend.java
index 8895a2c8..9a94329a 100644
--- a/src/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/eu/siacs/conversations/persistance/FileBackend.java
@@ -261,7 +261,7 @@ public class FileBackend {
mDigestOutputStream.write(avatar.getImageAsBytes());
mDigestOutputStream.flush();
mDigestOutputStream.close();
- Log.d("xmppService","sha1sum after write: "+CryptoHelper.bytesToHex(digest.digest()));
+ avatar.size = file.length();
} catch (FileNotFoundException e) {
} catch (IOException e) {
diff --git a/src/eu/siacs/conversations/services/Defaults.java b/src/eu/siacs/conversations/services/Defaults.java
new file mode 100644
index 00000000..c942dd48
--- /dev/null
+++ b/src/eu/siacs/conversations/services/Defaults.java
@@ -0,0 +1,11 @@
+package eu.siacs.conversations.services;
+
+import android.graphics.Bitmap;
+
+public final class Defaults {
+ public static final int AVATAR_SIZE = 192;
+ public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP;
+ private Defaults() {
+
+ }
+}
diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java
index 4ba0954f..c750ed53 100644
--- a/src/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/eu/siacs/conversations/services/XmppConnectionService.java
@@ -20,6 +20,7 @@ import de.duenndns.ssl.MemorizingTrustManager;
import net.java.otr4j.OtrException;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
+import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
@@ -1186,13 +1187,46 @@ public class XmppConnectionService extends Service {
}
- public void pushAvatar(Account account, Uri image) {
- Avatar avatar = getFileBackend().getPepAvatar(image, 192, Bitmap.CompressFormat.WEBP);
+ public void publishAvatar(Account account, Uri image, final UiCallback callback) {
+ final Bitmap.CompressFormat format = Defaults.AVATAR_FORMAT;
+ final int size = Defaults.AVATAR_SIZE;
+ final Avatar avatar = getFileBackend().getPepAvatar(image, size, format);
if (avatar!=null) {
- Log.d(LOGTAG,avatar.sha1sum);
- Log.d(LOGTAG,avatar.image);
- avatar.type = "image/webp";
+ avatar.height = size;
+ avatar.width = size;
+ if (format.equals(Bitmap.CompressFormat.WEBP)) {
+ avatar.type = "image/webp";
+ } else if (format.equals(Bitmap.CompressFormat.JPEG)) {
+ avatar.type = "image/jpeg";
+ } else if (format.equals(Bitmap.CompressFormat.PNG)) {
+ avatar.type = "image/png";
+ }
getFileBackend().save(avatar);
+ IqPacket packet = this.mIqGenerator.publishAvatar(avatar);
+ this.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ if (result.getType() == IqPacket.TYPE_RESULT) {
+ IqPacket packet = XmppConnectionService.this.mIqGenerator.publishAvatarMetadata(avatar);
+ sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ if (result.getType() == IqPacket.TYPE_RESULT) {
+ callback.success(avatar);
+ } else {
+ callback.error(R.string.error, avatar);
+ }
+ }
+ });
+ } else {
+ callback.error(R.string.error, avatar);
+ }
+ }
+ });
+ } else {
+ callback.error(R.string.error, null);
}
}
diff --git a/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
index 776ed774..a2cc84a6 100644
--- a/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
+++ b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
@@ -1,10 +1,11 @@
package eu.siacs.conversations.ui;
+import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
-import android.util.Log;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
@@ -13,47 +14,69 @@ import android.widget.TextView;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.PhoneHelper;
+import eu.siacs.conversations.xmpp.pep.Avatar;
public class PublishProfilePictureActivity extends XmppActivity {
-
+
private static final int REQUEST_CHOOSE_FILE = 0xac23;
-
+
private ImageView avatar;
- private TextView explanation;
+ private TextView accountTextView;
private Button cancelButton;
private Button publishButton;
-
+
private Uri avatarUri;
-
+
private Account account;
-
+
+ private UiCallback avatarPublication = new UiCallback() {
+
+ @Override
+ public void success(Avatar object) {
+ finish();
+ }
+
+ @Override
+ public void error(int errorCode, Avatar object) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Avatar object) {
+ // TODO Auto-generated method stub
+
+ }
+ };
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_publish_profile_picture);
this.avatar = (ImageView) findViewById(R.id.account_image);
- this.explanation = (TextView) findViewById(R.id.explanation);
this.cancelButton = (Button) findViewById(R.id.cancel_button);
this.publishButton = (Button) findViewById(R.id.publish_button);
+ this.accountTextView = (TextView) findViewById(R.id.account);
this.publishButton.setOnClickListener(new OnClickListener() {
-
+
@Override
public void onClick(View v) {
- if (avatarUri!=null) {
- xmppConnectionService.pushAvatar(account, avatarUri);
- finish();
+ if (avatarUri != null) {
+ disablePublishButton();
+ xmppConnectionService.publishAvatar(account, avatarUri,
+ avatarPublication);
}
}
});
this.cancelButton.setOnClickListener(new OnClickListener() {
-
+
@Override
public void onClick(View v) {
finish();
}
});
this.avatar.setOnClickListener(new OnClickListener() {
-
+
@Override
public void onClick(View v) {
Intent attachFileIntent = new Intent();
@@ -65,45 +88,61 @@ public class PublishProfilePictureActivity extends XmppActivity {
}
});
}
-
+
@Override
protected void onActivityResult(int requestCode, int resultCode,
final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_CHOOSE_FILE) {
- Log.d("xmppService","bla");
this.avatarUri = data.getData();
}
}
}
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ super.onOptionsItemSelected(menuItem);
+ switch (menuItem.getItemId()) {
+ case android.R.id.home:
+ finish();
+ break;
+ }
+ return true;
+ }
@Override
protected void onBackendConnected() {
- if (getIntent()!=null) {
+ if (getIntent() != null) {
String jid = getIntent().getStringExtra("account");
- if (jid!=null) {
+ if (jid != null) {
this.account = xmppConnectionService.findAccountByJid(jid);
if (this.avatarUri == null) {
- avatarUri = PhoneHelper.getSefliUri(getApplicationContext());
+ avatarUri = PhoneHelper
+ .getSefliUri(getApplicationContext());
}
loadImageIntoPreview(avatarUri);
- String explainText = getString(R.string.publish_avatar_explanation,account.getJid());
- this.explanation.setText(explainText);
+ this.accountTextView.setText(this.account.getJid());
}
}
-
+
}
-
+
protected void loadImageIntoPreview(Uri uri) {
- Bitmap bm = xmppConnectionService.getFileBackend().cropCenterSquare(uri, 384);
+ Bitmap bm = xmppConnectionService.getFileBackend().cropCenterSquare(
+ uri, 384);
this.avatar.setImageBitmap(bm);
enablePublishButton();
}
-
+
protected void enablePublishButton() {
this.publishButton.setEnabled(true);
this.publishButton.setTextColor(getPrimaryTextColor());
}
+
+ protected void disablePublishButton() {
+ this.publishButton.setEnabled(false);
+ this.publishButton.setTextColor(getSecondaryTextColor());
+ }
}
diff --git a/src/eu/siacs/conversations/xml/Element.java b/src/eu/siacs/conversations/xml/Element.java
index f8e070f7..3d22ba6a 100644
--- a/src/eu/siacs/conversations/xml/Element.java
+++ b/src/eu/siacs/conversations/xml/Element.java
@@ -144,4 +144,12 @@ public class Element {
public void clearChildren() {
this.children.clear();
}
+
+ public void setAttribute(String name, long value) {
+ this.setAttribute(name, ""+value);
+ }
+
+ public void setAttribute(String name, int value) {
+ this.setAttribute(name, ""+value);
+ }
}
diff --git a/src/eu/siacs/conversations/xmpp/pep/Avatar.java b/src/eu/siacs/conversations/xmpp/pep/Avatar.java
index 196c876c..b000c055 100644
--- a/src/eu/siacs/conversations/xmpp/pep/Avatar.java
+++ b/src/eu/siacs/conversations/xmpp/pep/Avatar.java
@@ -6,6 +6,9 @@ public class Avatar {
public String type;
public String sha1sum;
public String image;
+ public int height;
+ public int width;
+ public long size;
public byte[] getImageAsBytes() {
return Base64.decode(image, Base64.DEFAULT);
}
diff --git a/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java
index 64a9edc3..433b08c9 100644
--- a/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java
+++ b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java
@@ -8,6 +8,7 @@ public class MessagePacket extends AbstractStanza {
public static final int TYPE_NORMAL = 2;
public static final int TYPE_GROUPCHAT = 3;
public static final int TYPE_ERROR = 4;
+ public static final int TYPE_HEADLINE = 5;
public MessagePacket() {
super("message");
@@ -59,6 +60,8 @@ public class MessagePacket extends AbstractStanza {
return TYPE_GROUPCHAT;
} else if (type.equals("error")) {
return TYPE_ERROR;
+ } else if (type.equals("headline")) {
+ return TYPE_HEADLINE;
} else {
return TYPE_UNKNOWN;
}