jingle connection and manager. able to trigger dialog in gajim

This commit is contained in:
Daniel Gultsch 2014-04-07 20:05:45 +02:00
parent c30bf75a5d
commit 3f403fb8a9
10 changed files with 288 additions and 82 deletions

View file

@ -26,7 +26,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:maxHeight="300dp" android:maxHeight="288dp"
android:maxWidth="288dp"
/> />
<TextView <TextView

View file

@ -25,7 +25,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:maxHeight="300dp" android:maxHeight="288dp"
android:maxWidth="288dp"
/> />
<TextView <TextView

View file

@ -6,63 +6,80 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.math.BigInteger;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import android.util.LruCache;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
public class FileBackend { public class FileBackend {
private static int IMAGE_SIZE = 1920; private static int IMAGE_SIZE = 1920;
private Context context; private Context context;
private LruCache<String, Bitmap> thumbnailCache;
public FileBackend(Context context) { public FileBackend(Context context) {
this.context = context; this.context = context;
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
thumbnailCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount() / 1024;
}
};
} }
private File getImageFile(Message message) { public File getImageFile(Message message) {
Conversation conversation = message.getConversation(); Conversation conversation = message.getConversation();
String prefix = context.getFilesDir().getAbsolutePath(); String prefix = context.getFilesDir().getAbsolutePath();
String path = prefix+"/"+conversation.getAccount().getJid()+"/"+conversation.getContactJid(); String path = prefix + "/" + conversation.getAccount().getJid() + "/"
+ conversation.getContactJid();
String filename = message.getUuid() + ".webp"; String filename = message.getUuid() + ".webp";
return new File(path + "/" + filename); return new File(path + "/" + filename);
} }
private Bitmap resize(Bitmap originalBitmap, int size) {
int w = originalBitmap.getWidth();
int h = originalBitmap.getHeight();
if (Math.max(w, h) > size) {
int scalledW;
int scalledH;
if (w <= h) {
scalledW = (int) (w / ((double) h / size));
scalledH = size;
} else {
scalledW = size;
scalledH = (int) (h / ((double) w / size));
}
Bitmap scalledBitmap = Bitmap.createScaledBitmap(
originalBitmap, scalledW, scalledH, true);
return scalledBitmap;
} else {
return originalBitmap;
}
}
public File copyImageToPrivateStorage(Message message, Uri image) { public File copyImageToPrivateStorage(Message message, Uri image) {
try { try {
InputStream is = context.getContentResolver().openInputStream(image); InputStream is = context.getContentResolver()
.openInputStream(image);
File file = getImageFile(message); File file = getImageFile(message);
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
file.createNewFile(); file.createNewFile();
OutputStream os = new FileOutputStream(file); OutputStream os = new FileOutputStream(file);
Bitmap originalBitmap = BitmapFactory.decodeStream(is); Bitmap originalBitmap = BitmapFactory.decodeStream(is);
is.close(); is.close();
int w = originalBitmap.getWidth(); Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE);
int h = originalBitmap.getHeight(); boolean success = scalledBitmap.compress(Bitmap.CompressFormat.WEBP,75,os);
boolean success;
if (Math.max(w, h) > IMAGE_SIZE) {
int scalledW;
int scalledH;
if (w<=h) {
scalledW = (int) (w / ((double) h/IMAGE_SIZE));
scalledH = IMAGE_SIZE;
} else {
scalledW = IMAGE_SIZE;
scalledH = (int) (h / ((double) w/IMAGE_SIZE));
}
Bitmap scalledBitmap = Bitmap.createScaledBitmap(originalBitmap, scalledW,scalledH, true);
success = scalledBitmap.compress(Bitmap.CompressFormat.WEBP, 75, os);
} else {
success = originalBitmap.compress(Bitmap.CompressFormat.WEBP, 75, os);
}
if (!success) { if (!success) {
Log.d("xmppService", "couldnt compress"); Log.d("xmppService", "couldnt compress");
} }
@ -79,8 +96,20 @@ public class FileBackend {
return null; return null;
} }
public Bitmap getImageFromMessage(Message message) { public Bitmap getImageFromMessage(Message message) {
return BitmapFactory.decodeFile(getImageFile(message).getAbsolutePath()); return BitmapFactory
.decodeFile(getImageFile(message).getAbsolutePath());
}
public Bitmap getThumbnailFromMessage(Message message, int size) {
Bitmap thumbnail = thumbnailCache.get(message.getUuid());
if (thumbnail==null) {
Log.d("xmppService","creating new thumbnail" + message.getUuid());
Bitmap fullsize = BitmapFactory.decodeFile(getImageFile(message)
.getAbsolutePath());
thumbnail = resize(fullsize, size);
this.thumbnailCache.put(message.getUuid(), thumbnail);
}
return thumbnail;
} }
} }

View file

@ -0,0 +1,50 @@
package eu.siacs.conversations.services;
import java.util.concurrent.ConcurrentHashMap;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.JingleConnection;
import eu.siacs.conversations.xmpp.stanzas.jingle.JinglePacket;
public class JingleConnectionManager {
private XmppConnectionService xmppConnectionService;
private ConcurrentHashMap<String, JingleConnection> connections = new ConcurrentHashMap<String, JingleConnection>();
public JingleConnectionManager(XmppConnectionService service) {
this.xmppConnectionService = service;
}
public void deliverPacket(Account account, JinglePacket packet) {
String id = generateInternalId(account.getJid(), packet.getFrom(), packet.getSessionId());
}
public JingleConnection createNewConnection(Message message) {
Account account = message.getConversation().getAccount();
JingleConnection connection = new JingleConnection(this,account, message.getCounterpart());
String id = generateInternalId(account.getJid(), message.getCounterpart(), connection.getSessionId());
connection.init(message);
return connection;
}
private String generateInternalId(String account, String counterpart, String sid) {
return account+"#"+counterpart+"#"+sid;
}
public XmppConnectionService getXmppConnectionService() {
return this.xmppConnectionService;
}
public Element getPrimaryCanditate(String jid) {
Element canditate = new Element("canditate");
canditate.setAttribute("cid","122");
canditate.setAttribute("port","1234");
canditate.setAttribute("jid", jid);
canditate.setAttribute("type", "assisted");
return canditate;
}
}

View file

@ -87,6 +87,7 @@ public class XmppConnectionService extends Service {
private List<Account> accounts; private List<Account> accounts;
private List<Conversation> conversations = null; private List<Conversation> conversations = null;
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(this);
public OnConversationListChangedListener convChangedListener = null; public OnConversationListChangedListener convChangedListener = null;
private int convChangedListenerCount = 0; private int convChangedListenerCount = 0;
@ -389,13 +390,15 @@ public class XmppConnectionService extends Service {
return this.fileBackend; return this.fileBackend;
} }
public void attachImageToConversation(Conversation conversation, Uri uri) { public Message attachImageToConversation(Conversation conversation, Uri uri) {
Message message = new Message(conversation, "", Message.ENCRYPTION_NONE); Message message = new Message(conversation, "", Message.ENCRYPTION_NONE);
message.setType(Message.TYPE_IMAGE); message.setType(Message.TYPE_IMAGE);
File file = this.fileBackend.copyImageToPrivateStorage(message, uri); File file = this.fileBackend.copyImageToPrivateStorage(message, uri);
Log.d(LOGTAG,"new file"+file.getAbsolutePath()); Log.d(LOGTAG,"new file"+file.getAbsolutePath());
conversation.getMessages().add(message); conversation.getMessages().add(message);
databaseBackend.createMessage(message); databaseBackend.createMessage(message);
sendMessage(message, null);
return message;
} }
@ -655,6 +658,9 @@ public class XmppConnectionService extends Service {
boolean saveInDb = false; boolean saveInDb = false;
boolean addToConversation = false; boolean addToConversation = false;
if (account.getStatus() == Account.STATUS_ONLINE) { if (account.getStatus() == Account.STATUS_ONLINE) {
if (message.getType() == Message.TYPE_IMAGE) {
mJingleConnectionManager.createNewConnection(message);
} else {
MessagePacket packet; MessagePacket packet;
if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (message.getEncryption() == Message.ENCRYPTION_OTR) {
if (!conv.hasValidOtrSession()) { if (!conv.hasValidOtrSession()) {
@ -698,6 +704,7 @@ public class XmppConnectionService extends Service {
packet = prepareMessagePacket(account, message, null); packet = prepareMessagePacket(account, message, null);
account.getXmppConnection().sendMessagePacket(packet); account.getXmppConnection().sendMessagePacket(packet);
} }
}
} else { } else {
// account is offline // account is offline
saveInDb = true; saveInDb = true;

View file

@ -33,6 +33,7 @@ import android.graphics.Typeface;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -148,6 +149,8 @@ public class ConversationFragment extends Fragment {
public View onCreateView(final LayoutInflater inflater, public View onCreateView(final LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
this.inflater = inflater; this.inflater = inflater;
final View view = inflater.inflate(R.layout.fragment_conversation, final View view = inflater.inflate(R.layout.fragment_conversation,
@ -264,7 +267,7 @@ public class ConversationFragment extends Fragment {
} }
if (item.getType() == Message.TYPE_IMAGE) { if (item.getType() == Message.TYPE_IMAGE) {
viewHolder.image.setVisibility(View.VISIBLE); viewHolder.image.setVisibility(View.VISIBLE);
viewHolder.image.setImageBitmap(activity.xmppConnectionService.getFileBackend().getImageFromMessage(item)); viewHolder.image.setImageBitmap(activity.xmppConnectionService.getFileBackend().getThumbnailFromMessage(item,(int) (metrics.density * 288)));
viewHolder.messageBody.setVisibility(View.GONE); viewHolder.messageBody.setVisibility(View.GONE);
} else { } else {
if (viewHolder.image != null) viewHolder.image.setVisibility(View.GONE); if (viewHolder.image != null) viewHolder.image.setVisibility(View.GONE);

View file

@ -139,4 +139,8 @@ public class Element {
content = content.replace("'","&apos;"); content = content.replace("'","&apos;");
return content; return content;
} }
public void clearChildren() {
this.children.clear();
}
} }

View file

@ -0,0 +1,67 @@
package eu.siacs.conversations.xmpp;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import android.util.Log;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.JingleConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.stanzas.jingle.Content;
import eu.siacs.conversations.xmpp.stanzas.jingle.JinglePacket;
public class JingleConnection {
private JingleConnectionManager mJingleConnectionManager;
private XmppConnectionService mXmppConnectionService;
private String sessionId;
private Account account;
private String counterpart;
private List<Element> canditates = new ArrayList<Element>();
public JingleConnection(JingleConnectionManager mJingleConnectionManager, Account account, String counterpart) {
this.mJingleConnectionManager = mJingleConnectionManager;
this.mXmppConnectionService = mJingleConnectionManager.getXmppConnectionService();
this.account = account;
this.counterpart = counterpart;
SecureRandom random = new SecureRandom();
sessionId = new BigInteger(100, random).toString(32);
this.canditates.add(this.mJingleConnectionManager.getPrimaryCanditate(account.getJid()));
}
public String getSessionId() {
return this.sessionId;
}
public void init(Message message) {
JinglePacket packet = this.bootstrapPacket();
packet.setAction("session-initiate");
packet.setInitiator(this.account.getFullJid());
Content content = new Content();
if (message.getType() == Message.TYPE_IMAGE) {
//creator='initiator' name='a-file-offer'
content.setAttribute("creator", "initiator");
content.setAttribute("name", "a-file-offer");
content.offerFile(this.mXmppConnectionService.getFileBackend().getImageFile(message));
content.setCanditates(this.canditates);
packet.setContent(content);
Log.d("xmppService",packet.toString());
account.getXmppConnection().sendIqPacket(packet, null);
}
}
private JinglePacket bootstrapPacket() {
JinglePacket packet = new JinglePacket();
packet.setFrom(account.getFullJid());
packet.setTo(this.counterpart+"/Gajim");
packet.setSessionId(this.sessionId);
return packet;
}
}

View file

@ -1,5 +1,8 @@
package eu.siacs.conversations.xmpp.stanzas.jingle; package eu.siacs.conversations.xmpp.stanzas.jingle;
import java.io.File;
import java.util.List;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
public class Content extends Element { public class Content extends Element {
@ -10,4 +13,23 @@ public class Content extends Element {
public Content() { public Content() {
super("content"); super("content");
} }
public void offerFile(File actualFile) {
Element description = this.addChild("description", "urn:xmpp:jingle:apps:file-transfer:3");
Element offer = description.addChild("offer");
Element file = offer.addChild("file");
file.addChild("size").setContent(""+actualFile.length());
file.addChild("name").setContent(actualFile.getName());
}
public void setCanditates(List<Element> canditates) {
Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1");
if (transport==null) {
transport = this.addChild("transport", "urn:xmpp:jingle:transports:s5b:1");
}
transport.clearChildren();
for(Element canditate : canditates) {
transport.addChild(canditate);
}
}
} }

View file

@ -6,6 +6,7 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class JinglePacket extends IqPacket { public class JinglePacket extends IqPacket {
Content content = null; Content content = null;
Reason reason = null; Reason reason = null;
Element jingle = new Element("jingle");
@Override @Override
public Element addChild(Element child) { public Element addChild(Element child) {
@ -22,27 +23,25 @@ public class JinglePacket extends IqPacket {
this.reason.setChildren(reasonElement.getChildren()); this.reason.setChildren(reasonElement.getChildren());
this.reason.setAttributes(reasonElement.getAttributes()); this.reason.setAttributes(reasonElement.getAttributes());
} }
this.build(); this.jingle.setAttributes(child.getAttributes());
this.findChild("jingle").setAttributes(child.getAttributes());
} }
return child; return child;
} }
public JinglePacket setContent(Content content) { public JinglePacket setContent(Content content) {
this.content = content; this.content = content;
this.build();
return this; return this;
} }
public JinglePacket setReason(Reason reason) { public JinglePacket setReason(Reason reason) {
this.reason = reason; this.reason = reason;
this.build();
return this; return this;
} }
private void build() { private void build() {
this.children.clear(); this.children.clear();
Element jingle = addChild("jingle", "urn:xmpp:jingle:1"); this.jingle.clearChildren();
this.jingle.setAttribute("xmlns", "urn:xmpp:jingle:1");
if (this.content!=null) { if (this.content!=null) {
jingle.addChild(this.content); jingle.addChild(this.content);
} }
@ -50,5 +49,28 @@ public class JinglePacket extends IqPacket {
jingle.addChild(this.reason); jingle.addChild(this.reason);
} }
this.children.add(jingle); this.children.add(jingle);
this.setAttribute("type", "set");
}
public String getSessionId() {
return this.jingle.getAttribute("sid");
}
public void setSessionId(String sid) {
this.jingle.setAttribute("sid", sid);
}
@Override
public String toString() {
this.build();
return super.toString();
}
public void setAction(String action) {
this.jingle.setAttribute("action", action);
}
public void setInitiator(String initiator) {
this.jingle.setAttribute("initiator", initiator);
} }
} }