From 3d920d81138a0a306de711d9c1a043ecbfc4fe6f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Anthony=20L=C3=A9onard?=
 <anthony.leonard@savoirfairelinux.com>
Date: Mon, 31 Jul 2017 13:51:16 -0400
Subject: [PATCH] modernize avatar placeholder
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Default avatar displayed for new contacts is replaced by a more modern
color circle with a capital letter inside.

The color palette used is the same as in the GNOME client. It comes
from the material.io website and is a 16 color subset of the full one
proposed by Google.

The letter is the first one of the bestName() corresponding to the
current item. The “Segoe UI” font is used as recommended by Windows
design guidelines.

Change-Id: I4c8c5729a77c0ff52d18de33c8e2fb8b5b1d7a88
Reviewed-by: Olivier Soldano <olivier.soldano@savoirfairelinux.com>
---
 pixbufmanipulator.cpp | 60 ++++++++++++++++++++++++++++++++++---------
 pixbufmanipulator.h   |  4 +--
 ringthemeutils.h      | 19 ++++++++++++++
 3 files changed, 68 insertions(+), 15 deletions(-)

diff --git a/pixbufmanipulator.cpp b/pixbufmanipulator.cpp
index 0f774ad..65a6e4f 100644
--- a/pixbufmanipulator.cpp
+++ b/pixbufmanipulator.cpp
@@ -26,6 +26,7 @@
 #include <QIODevice>
 #include <QByteArray>
 #include <QBuffer>
+#include <QPainter>
 
 #include "person.h"
 #include "call.h"
@@ -34,7 +35,7 @@
 #include "profile.h"
 
 #include "utils.h"
-
+#include "ringthemeutils.h"
 #undef interface
 
 /*Namespace Interfaces collide with QBuffer*/
@@ -49,10 +50,6 @@ QByteArray QImageToByteArray(QImage image)
 
 namespace Interfaces {
 
-PixbufManipulator::PixbufManipulator()
-    : fallbackAvatar_(QImage(":/images/user/btn-default-userpic.svg"))
-{}
-
 QImage
 PixbufManipulator::scaleAndFrame(const QImage photo, const QSize& size)
 {
@@ -63,7 +60,9 @@ QVariant
 PixbufManipulator::callPhoto(Call* c, const QSize& size, bool displayPresence)
 {
     if (!c || c->type() == Call::Type::CONFERENCE){
-        return QVariant::fromValue(scaleAndFrame(fallbackAvatar_, size));
+        return QVariant::fromValue(fallbackAvatar(size,
+                                                  c->peerContactMethod()->uri().userinfo().at(0).toLatin1(),
+                                                  c->peerContactMethod()->bestName().at(0).toUpper().toLatin1()));
     }
     return callPhoto(c->peerContactMethod(), size, displayPresence);
 }
@@ -74,7 +73,9 @@ PixbufManipulator::callPhoto(const ContactMethod* n, const QSize& size, bool dis
     if (n && n->contact()) {
         return contactPhoto(n->contact(), size, displayPresence);
     } else {
-        return QVariant::fromValue(scaleAndFrame(fallbackAvatar_, size));
+        return QVariant::fromValue(fallbackAvatar(size,
+                                                  n->uri().userinfo().at(0).toLatin1(),
+                                                  n->bestName().at(0).toUpper().toLatin1()));
     }
 }
 
@@ -93,7 +94,9 @@ PixbufManipulator::contactPhoto(Person* c, const QSize& size, bool displayPresen
     if (c->photo().isValid()){
         photo = c->photo().value<QImage>();
     } else {
-        photo = fallbackAvatar_;
+        photo = fallbackAvatar(size,
+                               c->phoneNumbers().at(0)->uri().userinfo().at(0).toLatin1(),
+                               c->phoneNumbers().at(0)->bestName().at(0).toUpper().toLatin1());
     }
     return QVariant::fromValue(scaleAndFrame(photo, size));
 }
@@ -105,7 +108,7 @@ QVariant PixbufManipulator::personPhoto(const QByteArray& data, const QString& t
     const char* c_str2 = ba.data();
     if (avatar.loadFromData(data.fromBase64(data), c_str2))
         return Utils::getCirclePhoto(avatar, avatar.size().width());
-    return fallbackAvatar_;
+    return fallbackAvatar(imgSize_, '?', '?');
 }
 
 QVariant
@@ -182,7 +185,9 @@ QVariant PixbufManipulator::decorationRole(const Call* c)
         photo =  c->peerContactMethod()->contact()->photo().value<QImage>();
     }
     else
-        photo = fallbackAvatar_;
+        photo = fallbackAvatar(imgSize_,
+                               c->peerContactMethod()->uri().userinfo().at(0).toLatin1(),
+                               c->peerContactMethod()->bestName().at(0).toUpper().toLatin1());
     return QVariant::fromValue(scaleAndFrame(photo, imgSize_));
 }
 
@@ -192,7 +197,9 @@ QVariant PixbufManipulator::decorationRole(const ContactMethod* cm)
     if (cm && cm->contact() && cm->contact()->photo().isValid())
         photo = cm->contact()->photo().value<QImage>();
     else
-        photo = fallbackAvatar_;
+        photo = fallbackAvatar(imgSize_,
+                               cm->uri().userinfo().at(0).toLatin1(),
+                               cm->bestName().at(0).toUpper().toLatin1());
     return QVariant::fromValue(scaleAndFrame(photo, imgSize_));
 }
 
@@ -202,7 +209,9 @@ QVariant PixbufManipulator::decorationRole(const Person* p)
     if (p && p->photo().isValid())
         photo = p->photo().value<QImage>();
     else
-        photo = fallbackAvatar_;
+        photo = fallbackAvatar(imgSize_,
+                               p->phoneNumbers().at(0)->uri().userinfo().at(0).toLatin1(),
+                               p->phoneNumbers().at(0)->bestName().at(0).toUpper().toLatin1());
     return QVariant::fromValue(scaleAndFrame(photo, imgSize_));
 }
 
@@ -214,4 +223,31 @@ QVariant PixbufManipulator::decorationRole(const Account* acc)
                                      imgSize_.width());
 }
 
+QImage PixbufManipulator::fallbackAvatar(const QSize size, const char color, const char letter)
+{
+    // We start with a transparent avatar
+    QImage avatar(size, QImage::Format_ARGB32);
+    avatar.fill(Qt::transparent);
+
+    // We pick a color based on the passed character
+    QColor avColor = RingTheme::avatarColors_[color % 16];
+
+    // We draw a circle with this color
+    QPainter painter(&avatar);
+    painter.setRenderHints(QPainter::Antialiasing|QPainter::SmoothPixmapTransform);
+    painter.setPen(Qt::transparent);
+    painter.setBrush(avColor);
+    painter.drawEllipse(avatar.rect());
+
+    // Then we paint a letter in the circle
+    QFont segoeFont("Segoe UI", avatar.height()/2); // We use Segoe UI as recommended by Windows guidelines
+    painter.setFont(segoeFont);
+    painter.setPen(Qt::white);
+    QRect textRect = avatar.rect();
+    textRect.moveTop(textRect.top()-(avatar.height()/20)); // Empirical value that seems to correct centering nicely
+    painter.drawText(textRect, QString(letter), QTextOption(Qt::AlignCenter));
+
+    return avatar;
+}
+
 } // namespace Interfaces
diff --git a/pixbufmanipulator.h b/pixbufmanipulator.h
index 7ebc92b..2abbc9a 100644
--- a/pixbufmanipulator.h
+++ b/pixbufmanipulator.h
@@ -33,8 +33,6 @@ namespace Interfaces {
 
 class PixbufManipulator : public PixmapManipulatorI {
 public:
-    PixbufManipulator();
-
     QVariant callPhoto(Call* c, const QSize& size, bool displayPresence = true) override;
     QVariant callPhoto(const ContactMethod* n, const QSize& size, bool displayPresence = true) override;
     QVariant contactPhoto(Person* c, const QSize& size, bool displayPresence = true) override;
@@ -56,9 +54,9 @@ public:
     QVariant   decorationRole(const Account* acc) override;
 
 private:
-    QImage fallbackAvatar_;
     QImage scaleAndFrame(const QImage photo, const QSize& size);
     const QSize imgSize_ {48, 48};
+    static QImage fallbackAvatar(const QSize size, const char color, const char letter);
 };
 
 } // namespace Interfaces
diff --git a/ringthemeutils.h b/ringthemeutils.h
index 659fe9c..c5f6896 100644
--- a/ringthemeutils.h
+++ b/ringthemeutils.h
@@ -32,4 +32,23 @@ static const QColor lightRed_ {252, 91, 90};
 static const QColor darkRed_ {219, 55, 54};
 static const QColor green_ {127, 255, 0};
 
+static const QColor avatarColors_[] {
+    {"#fff44336"}, //Red
+    {"#ffe91e63"}, //Pink
+    {"#ff9c27b0"}, //Purple
+    {"#ff673ab7"}, //Deep Purple
+    {"#ff3f51b5"}, //Indigo
+    {"#ff2196f3"}, //Blue
+    {"#ff00bcd4"}, //Cyan
+    {"#ff009688"}, //Teal
+    {"#ff4caf50"}, //Green
+    {"#ff8bc34a"}, //Light Green
+    {"#ff9e9e9e"}, //Grey
+    {"#ffcddc39"}, //Lime
+    {"#ffffc107"}, //Amber
+    {"#ffff5722"}, //Deep Orange
+    {"#ff795548"}, //Brown
+    {"#ff607d8b"}  //Blue Grey
+};
+
 }
-- 
GitLab