2017-03-02 14:37:32 +00:00
|
|
|
using Gdk;
|
|
|
|
using Gee;
|
|
|
|
|
|
|
|
using Xmpp;
|
|
|
|
using Dino.Entities;
|
|
|
|
|
|
|
|
namespace Dino {
|
|
|
|
|
|
|
|
public class AvatarManager : StreamInteractionModule, Object {
|
2017-03-19 11:55:36 +00:00
|
|
|
public static ModuleIdentity<AvatarManager> IDENTITY = new ModuleIdentity<AvatarManager>("avatar_manager");
|
|
|
|
public string id { get { return IDENTITY.id; } }
|
2017-03-02 14:37:32 +00:00
|
|
|
|
|
|
|
public signal void received_avatar(Pixbuf avatar, Jid jid, Account account);
|
|
|
|
|
|
|
|
private enum Source {
|
|
|
|
USER_AVATARS,
|
|
|
|
VCARD
|
|
|
|
}
|
|
|
|
|
|
|
|
private StreamInteractor stream_interactor;
|
|
|
|
private Database db;
|
|
|
|
private HashMap<Jid, string> user_avatars = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func);
|
|
|
|
private HashMap<Jid, string> vcard_avatars = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func);
|
2017-03-12 12:19:04 +00:00
|
|
|
private AvatarStorage avatar_storage = new AvatarStorage(get_storage_dir());
|
2017-03-24 09:40:48 +00:00
|
|
|
private HashMap<string, Pixbuf> cached_pixbuf = new HashMap<string, Pixbuf>();
|
2017-03-02 14:37:32 +00:00
|
|
|
private const int MAX_PIXEL = 192;
|
|
|
|
|
|
|
|
public static void start(StreamInteractor stream_interactor, Database db) {
|
|
|
|
AvatarManager m = new AvatarManager(stream_interactor, db);
|
|
|
|
stream_interactor.add_module(m);
|
|
|
|
}
|
|
|
|
|
2017-03-12 12:19:04 +00:00
|
|
|
public static string get_storage_dir() {
|
2017-05-04 20:05:48 +00:00
|
|
|
return Path.build_filename(Dino.get_storage_dir(), "avatars");
|
2017-03-12 12:19:04 +00:00
|
|
|
}
|
|
|
|
|
2017-03-02 14:37:32 +00:00
|
|
|
private AvatarManager(StreamInteractor stream_interactor, Database db) {
|
|
|
|
this.stream_interactor = stream_interactor;
|
|
|
|
this.db = db;
|
|
|
|
stream_interactor.account_added.connect(on_account_added);
|
2017-03-12 12:19:04 +00:00
|
|
|
stream_interactor.module_manager.initialize_account_modules.connect(initialize_avatar_modules);
|
|
|
|
}
|
|
|
|
|
2018-01-12 20:03:09 +00:00
|
|
|
private void initialize_avatar_modules(Account account, ArrayList<XmppStreamModule> modules) {
|
2017-03-12 12:19:04 +00:00
|
|
|
modules.add(new Xep.UserAvatars.Module(avatar_storage));
|
|
|
|
modules.add(new Xep.VCard.Module(avatar_storage));
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2019-04-19 19:42:40 +00:00
|
|
|
private async Pixbuf? get_avatar_by_hash(string hash) {
|
2018-01-12 20:03:09 +00:00
|
|
|
if (cached_pixbuf.has_key(hash)) {
|
|
|
|
return cached_pixbuf[hash];
|
2017-03-24 09:40:48 +00:00
|
|
|
}
|
2019-04-19 19:42:40 +00:00
|
|
|
Pixbuf? image = yield avatar_storage.get_image(hash);
|
2017-03-24 09:40:48 +00:00
|
|
|
if (image != null) {
|
2018-01-12 20:03:09 +00:00
|
|
|
cached_pixbuf[hash] = image;
|
2019-05-29 14:52:36 +00:00
|
|
|
} else {
|
|
|
|
db.avatar.delete().with(db.avatar.hash, "=", hash).perform();
|
2017-03-24 09:40:48 +00:00
|
|
|
}
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
2019-04-19 19:42:40 +00:00
|
|
|
public bool has_avatar(Account account, Jid jid) {
|
|
|
|
string? hash = get_avatar_hash(account, jid);
|
|
|
|
if (hash != null) {
|
|
|
|
if (cached_pixbuf.has_key(hash)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return avatar_storage.has_image(hash);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Pixbuf? get_avatar(Account account, Jid jid) {
|
|
|
|
string? hash = get_avatar_hash(account, jid);
|
|
|
|
if (hash != null) {
|
|
|
|
return yield get_avatar_by_hash(hash);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private string? get_avatar_hash(Account account, Jid jid) {
|
2019-05-29 14:52:36 +00:00
|
|
|
string? user_avatars_id = user_avatars[jid];
|
2018-01-12 20:03:09 +00:00
|
|
|
if (user_avatars_id != null) {
|
2019-04-19 19:42:40 +00:00
|
|
|
return user_avatars_id;
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
2019-05-29 14:52:36 +00:00
|
|
|
string? vcard_avatars_id = vcard_avatars[jid];
|
2018-01-12 20:03:09 +00:00
|
|
|
if (vcard_avatars_id != null) {
|
2019-04-19 19:42:40 +00:00
|
|
|
return vcard_avatars_id;
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void publish(Account account, string file) {
|
|
|
|
try {
|
|
|
|
Pixbuf pixbuf = new Pixbuf.from_file(file);
|
|
|
|
if (pixbuf.width >= pixbuf.height && pixbuf.width > MAX_PIXEL) {
|
|
|
|
int dest_height = (int) ((float) MAX_PIXEL / pixbuf.width * pixbuf.height);
|
|
|
|
pixbuf = pixbuf.scale_simple(MAX_PIXEL, dest_height, InterpType.BILINEAR);
|
|
|
|
} else if (pixbuf.height > pixbuf.width && pixbuf.width > MAX_PIXEL) {
|
|
|
|
int dest_width = (int) ((float) MAX_PIXEL / pixbuf.height * pixbuf.width);
|
|
|
|
pixbuf = pixbuf.scale_simple(dest_width, MAX_PIXEL, InterpType.BILINEAR);
|
|
|
|
}
|
|
|
|
uint8[] buffer;
|
|
|
|
pixbuf.save_to_buffer(out buffer, "png");
|
2018-01-12 20:03:09 +00:00
|
|
|
XmppStream stream = stream_interactor.get_stream(account);
|
2017-03-02 14:37:32 +00:00
|
|
|
if (stream != null) {
|
2017-03-11 00:40:42 +00:00
|
|
|
stream.get_module(Xep.UserAvatars.Module.IDENTITY).publish_png(stream, buffer, pixbuf.width, pixbuf.height);
|
2017-03-02 14:37:32 +00:00
|
|
|
on_user_avatar_received(account, account.bare_jid, Base64.encode(buffer));
|
|
|
|
}
|
|
|
|
} catch (Error e) {
|
2019-03-15 19:56:19 +00:00
|
|
|
warning(e.message);
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void on_account_added(Account account) {
|
2017-03-10 20:45:56 +00:00
|
|
|
stream_interactor.module_manager.get_module(account, Xep.UserAvatars.Module.IDENTITY).received_avatar.connect((stream, jid, id) =>
|
2018-01-12 20:03:09 +00:00
|
|
|
on_user_avatar_received(account, jid, id)
|
2017-03-02 14:37:32 +00:00
|
|
|
);
|
2017-03-10 20:45:56 +00:00
|
|
|
stream_interactor.module_manager.get_module(account, Xep.VCard.Module.IDENTITY).received_avatar.connect((stream, jid, id) =>
|
2018-01-12 20:03:09 +00:00
|
|
|
on_vcard_avatar_received(account, jid, id)
|
2017-03-02 14:37:32 +00:00
|
|
|
);
|
|
|
|
|
2019-05-29 14:52:36 +00:00
|
|
|
foreach (var entry in db.get_avatar_hashes(Source.USER_AVATARS).entries) {
|
|
|
|
on_user_avatar_received(account, entry.key, entry.value);
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
2019-05-29 14:52:36 +00:00
|
|
|
foreach (var entry in db.get_avatar_hashes(Source.VCARD).entries) {
|
|
|
|
// FIXME: remove. temporary to remove falsely saved avatars.
|
|
|
|
if (stream_interactor.get_module(MucManager.IDENTITY).is_groupchat(entry.key, account)) {
|
|
|
|
db.avatar.delete().with(db.avatar.jid, "=", entry.key.to_string()).perform();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
on_vcard_avatar_received(account, entry.key, entry.value);
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void on_user_avatar_received(Account account, Jid jid, string id) {
|
2018-01-12 20:03:09 +00:00
|
|
|
if (!user_avatars.has_key(jid) || user_avatars[jid] != id) {
|
|
|
|
user_avatars[jid] = id;
|
|
|
|
db.set_avatar_hash(jid, id, Source.USER_AVATARS);
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
2019-04-19 19:42:40 +00:00
|
|
|
avatar_storage.get_image.begin(id, (obj, res) => {
|
|
|
|
Pixbuf? avatar = avatar_storage.get_image.end(res);
|
|
|
|
if (avatar != null) {
|
|
|
|
received_avatar(avatar, jid, account);
|
|
|
|
}
|
|
|
|
});
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void on_vcard_avatar_received(Account account, Jid jid, string id) {
|
2018-01-12 20:03:09 +00:00
|
|
|
if (!vcard_avatars.has_key(jid) || vcard_avatars[jid] != id) {
|
|
|
|
vcard_avatars[jid] = id;
|
2019-05-29 14:52:36 +00:00
|
|
|
if (!jid.is_full()) { // don't save MUC occupant avatars
|
2018-01-12 20:03:09 +00:00
|
|
|
db.set_avatar_hash(jid, id, Source.VCARD);
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-19 19:42:40 +00:00
|
|
|
avatar_storage.get_image.begin(id, (obj, res) => {
|
|
|
|
Pixbuf? avatar = avatar_storage.get_image.end(res);
|
|
|
|
if (avatar != null) {
|
|
|
|
received_avatar(avatar, jid, account);
|
|
|
|
}
|
|
|
|
});
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-15 19:56:19 +00:00
|
|
|
}
|