publish avatars

This commit is contained in:
iNPUTmice 2014-08-05 01:36:17 +02:00
parent 629ff3afcd
commit f8b4d5f338
12 changed files with 197 additions and 35 deletions

View file

@ -76,12 +76,19 @@
android:paddingRight="8dp" > android:paddingRight="8dp" >
<TextView <TextView
android:id="@+id/explanation" android:id="@+id/account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/primarytext"
android:textSize="18sp"/>
<TextView
android:layout_marginTop="8dp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/publish_avatar_explanation" android:text="@string/publish_avatar_explanation"
android:textColor="@color/primarytext" android:textColor="@color/primarytext"
android:textSize="18sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>

View file

@ -270,5 +270,5 @@
<string name="contact_has_read_up_to_this_point">%s has read up to this point</string> <string name="contact_has_read_up_to_this_point">%s has read up to this point</string>
<string name="publish_avatar">Publish avatar</string> <string name="publish_avatar">Publish avatar</string>
<string name="touch_to_choose_picture">Touch avatar to select picture from gallary</string> <string name="touch_to_choose_picture">Touch avatar to select picture from gallary</string>
<string name="publish_avatar_explanation">Publish avatar for <b>%s</b>. Everyone subscribed to your presence updates will also be able to see this picture.</string> <string name="publish_avatar_explanation">Please note: Everyone subscribed to your presence updates will be allowed to see this picture.</string>
</resources> </resources>

View file

@ -18,7 +18,8 @@ public abstract class AbstractGenerator {
"http://jabber.org/protocol/muc", "http://jabber.org/protocol/muc",
"jabber:x:conference", "jabber:x:conference",
"http://jabber.org/protocol/caps", "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_NAME = "Conversations 0.5";
public final String IDENTITY_TYPE = "phone"; 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" }; /*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" };

View file

@ -5,6 +5,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class IqGenerator extends AbstractGenerator { public class IqGenerator extends AbstractGenerator {
@ -28,4 +29,34 @@ public class IqGenerator extends AbstractGenerator {
} }
return packet; 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);
}
} }

View file

@ -1,6 +1,7 @@
package eu.siacs.conversations.parser; package eu.siacs.conversations.parser;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log;
import net.java.otr4j.session.Session; import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus; import net.java.otr4j.session.SessionStatus;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
@ -213,6 +214,10 @@ public class MessageParser extends AbstractParser implements
} }
private void parseNormal(Element packet, Account account) { 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")) { if (packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
String id = packet String id = packet
.findChild("displayed", "urn:xmpp:chat-markers:0") .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) { private String getPgpBody(Element message) {
Element child = message.findChild("x", "jabber:x:encrypted"); Element child = message.findChild("x", "jabber:x:encrypted");
if (child == null) { if (child == null) {
@ -324,6 +339,9 @@ public class MessageParser extends AbstractParser implements
} else if (packet.getType() == MessagePacket.TYPE_NORMAL) { } else if (packet.getType() == MessagePacket.TYPE_NORMAL) {
this.parseNormal(packet, account); this.parseNormal(packet, account);
return; return;
} else if (packet.getType() == MessagePacket.TYPE_HEADLINE) {
this.parseHeadline(packet, account);
return;
} }
if ((message == null) || (message.getBody() == null)) { if ((message == null) || (message.getBody() == null)) {
return; return;
@ -346,4 +364,11 @@ public class MessageParser extends AbstractParser implements
} }
mXmppConnectionService.notifyUi(conversation, notify); 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);
}
}
} }

View file

@ -261,7 +261,7 @@ public class FileBackend {
mDigestOutputStream.write(avatar.getImageAsBytes()); mDigestOutputStream.write(avatar.getImageAsBytes());
mDigestOutputStream.flush(); mDigestOutputStream.flush();
mDigestOutputStream.close(); mDigestOutputStream.close();
Log.d("xmppService","sha1sum after write: "+CryptoHelper.bytesToHex(digest.digest())); avatar.size = file.length();
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
} catch (IOException e) { } catch (IOException e) {

View file

@ -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() {
}
}

View file

@ -20,6 +20,7 @@ import de.duenndns.ssl.MemorizingTrustManager;
import net.java.otr4j.OtrException; import net.java.otr4j.OtrException;
import net.java.otr4j.session.Session; import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus; import net.java.otr4j.session.SessionStatus;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Bookmark;
@ -1186,13 +1187,46 @@ public class XmppConnectionService extends Service {
} }
public void pushAvatar(Account account, Uri image) { public void publishAvatar(Account account, Uri image, final UiCallback<Avatar> callback) {
Avatar avatar = getFileBackend().getPepAvatar(image, 192, Bitmap.CompressFormat.WEBP); final Bitmap.CompressFormat format = Defaults.AVATAR_FORMAT;
final int size = Defaults.AVATAR_SIZE;
final Avatar avatar = getFileBackend().getPepAvatar(image, size, format);
if (avatar!=null) { if (avatar!=null) {
Log.d(LOGTAG,avatar.sha1sum); avatar.height = size;
Log.d(LOGTAG,avatar.image); avatar.width = size;
if (format.equals(Bitmap.CompressFormat.WEBP)) {
avatar.type = "image/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); 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);
} }
} }

View file

@ -1,10 +1,11 @@
package eu.siacs.conversations.ui; package eu.siacs.conversations.ui;
import android.app.PendingIntent;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.Button; import android.widget.Button;
@ -13,13 +14,14 @@ import android.widget.TextView;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.PhoneHelper; import eu.siacs.conversations.utils.PhoneHelper;
import eu.siacs.conversations.xmpp.pep.Avatar;
public class PublishProfilePictureActivity extends XmppActivity { public class PublishProfilePictureActivity extends XmppActivity {
private static final int REQUEST_CHOOSE_FILE = 0xac23; private static final int REQUEST_CHOOSE_FILE = 0xac23;
private ImageView avatar; private ImageView avatar;
private TextView explanation; private TextView accountTextView;
private Button cancelButton; private Button cancelButton;
private Button publishButton; private Button publishButton;
@ -27,21 +29,42 @@ public class PublishProfilePictureActivity extends XmppActivity {
private Account account; private Account account;
private UiCallback<Avatar> avatarPublication = new UiCallback<Avatar>() {
@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 @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_publish_profile_picture); setContentView(R.layout.activity_publish_profile_picture);
this.avatar = (ImageView) findViewById(R.id.account_image); this.avatar = (ImageView) findViewById(R.id.account_image);
this.explanation = (TextView) findViewById(R.id.explanation);
this.cancelButton = (Button) findViewById(R.id.cancel_button); this.cancelButton = (Button) findViewById(R.id.cancel_button);
this.publishButton = (Button) findViewById(R.id.publish_button); this.publishButton = (Button) findViewById(R.id.publish_button);
this.accountTextView = (TextView) findViewById(R.id.account);
this.publishButton.setOnClickListener(new OnClickListener() { this.publishButton.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (avatarUri != null) { if (avatarUri != null) {
xmppConnectionService.pushAvatar(account, avatarUri); disablePublishButton();
finish(); xmppConnectionService.publishAvatar(account, avatarUri,
avatarPublication);
} }
} }
}); });
@ -72,12 +95,22 @@ public class PublishProfilePictureActivity extends XmppActivity {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_CHOOSE_FILE) { if (requestCode == REQUEST_CHOOSE_FILE) {
Log.d("xmppService","bla");
this.avatarUri = data.getData(); 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 @Override
protected void onBackendConnected() { protected void onBackendConnected() {
if (getIntent() != null) { if (getIntent() != null) {
@ -85,18 +118,19 @@ public class PublishProfilePictureActivity extends XmppActivity {
if (jid != null) { if (jid != null) {
this.account = xmppConnectionService.findAccountByJid(jid); this.account = xmppConnectionService.findAccountByJid(jid);
if (this.avatarUri == null) { if (this.avatarUri == null) {
avatarUri = PhoneHelper.getSefliUri(getApplicationContext()); avatarUri = PhoneHelper
.getSefliUri(getApplicationContext());
} }
loadImageIntoPreview(avatarUri); loadImageIntoPreview(avatarUri);
String explainText = getString(R.string.publish_avatar_explanation,account.getJid()); this.accountTextView.setText(this.account.getJid());
this.explanation.setText(explainText);
} }
} }
} }
protected void loadImageIntoPreview(Uri uri) { protected void loadImageIntoPreview(Uri uri) {
Bitmap bm = xmppConnectionService.getFileBackend().cropCenterSquare(uri, 384); Bitmap bm = xmppConnectionService.getFileBackend().cropCenterSquare(
uri, 384);
this.avatar.setImageBitmap(bm); this.avatar.setImageBitmap(bm);
enablePublishButton(); enablePublishButton();
} }
@ -106,4 +140,9 @@ public class PublishProfilePictureActivity extends XmppActivity {
this.publishButton.setTextColor(getPrimaryTextColor()); this.publishButton.setTextColor(getPrimaryTextColor());
} }
protected void disablePublishButton() {
this.publishButton.setEnabled(false);
this.publishButton.setTextColor(getSecondaryTextColor());
}
} }

View file

@ -144,4 +144,12 @@ public class Element {
public void clearChildren() { public void clearChildren() {
this.children.clear(); 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);
}
} }

View file

@ -6,6 +6,9 @@ public class Avatar {
public String type; public String type;
public String sha1sum; public String sha1sum;
public String image; public String image;
public int height;
public int width;
public long size;
public byte[] getImageAsBytes() { public byte[] getImageAsBytes() {
return Base64.decode(image, Base64.DEFAULT); return Base64.decode(image, Base64.DEFAULT);
} }

View file

@ -8,6 +8,7 @@ public class MessagePacket extends AbstractStanza {
public static final int TYPE_NORMAL = 2; public static final int TYPE_NORMAL = 2;
public static final int TYPE_GROUPCHAT = 3; public static final int TYPE_GROUPCHAT = 3;
public static final int TYPE_ERROR = 4; public static final int TYPE_ERROR = 4;
public static final int TYPE_HEADLINE = 5;
public MessagePacket() { public MessagePacket() {
super("message"); super("message");
@ -59,6 +60,8 @@ public class MessagePacket extends AbstractStanza {
return TYPE_GROUPCHAT; return TYPE_GROUPCHAT;
} else if (type.equals("error")) { } else if (type.equals("error")) {
return TYPE_ERROR; return TYPE_ERROR;
} else if (type.equals("headline")) {
return TYPE_HEADLINE;
} else { } else {
return TYPE_UNKNOWN; return TYPE_UNKNOWN;
} }