Commit 25236d94 authored by Edric Milaret's avatar Edric Milaret

add profile feature

- Allow the user to choose a photo or take one
- Allow the user to modify profile in settings
- Link with LRC model to send profile to peer

Change-Id: I04054f98fbd756c0d08f0b69a4e7020d56bdccab
Tuleap: #531
parent a7cf8653
......@@ -67,7 +67,8 @@ SOURCES += main.cpp\
pixbufmanipulator.cpp \
qualitydialog.cpp \
ringbutton.cpp \
pathpassworddialog.cpp
pathpassworddialog.cpp \
photoboothdialog.cpp
HEADERS += mainwindow.h \
callwidget.h \
......@@ -103,7 +104,8 @@ HEADERS += mainwindow.h \
qualitydialog.h \
ringthemeutils.h \
ringbutton.h \
pathpassworddialog.h
pathpassworddialog.h \
photoboothdialog.h
contains(DEFINES, URI_PROTOCOL) {
HEADERS += shmclient.h
......@@ -125,9 +127,11 @@ FORMS += mainwindow.ui \
combar.ui \
qualitydialog.ui \
ringbutton.ui \
pathpassworddialog.ui
pathpassworddialog.ui \
photoboothdialog.ui
win32: LIBS += -lole32 -luuid -lshlwapi -lqrencode
win32: LIBS += -lole32 -luuid -lshlwapi
LIBS += -lqrencode
INCLUDEPATH += $${RING}/include/libringclient
INCLUDEPATH += $${RING}/include
......
......@@ -23,7 +23,6 @@
#include <QFileDialog>
#include <QPushButton>
#include "accountdetails.h"
#include "codecmodel.h"
#include "protocolmodel.h"
#include "certificate.h"
......@@ -112,6 +111,7 @@ AccountDetails::setAccount(Account* currentAccount) {
codecModel_ = currentAccount->codecModel();
ui->audioCodecView->setModel(codecModel_->audioCodecs());
ui->videoCodecView->setModel(codecModel_->videoCodecs());
connect(ui->audioCodecView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(audioCodecSelectionChanged(QItemSelection,QItemSelection)));
connect(ui->videoCodecView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
......@@ -298,4 +298,3 @@ AccountDetails::getDeleteAccountButton()
{
return ui->deleteAccountButton;
}
......@@ -190,7 +190,7 @@
<property name="verticalSpacing">
<number>10</number>
</property>
<item row="0" column="0">
<item row="1" column="0">
<widget class="QLabel" name="aliasLabel">
<property name="font">
<font>
......@@ -208,7 +208,7 @@
</property>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="1">
<widget class="QLineEdit" name="lrcfg_alias">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
......@@ -224,7 +224,7 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="typeLabel">
<property name="font">
<font>
......@@ -239,7 +239,7 @@
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QLabel" name="typeValueLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
......@@ -2394,7 +2394,7 @@
<customwidget>
<class>RingButton</class>
<extends>QPushButton</extends>
<header>ringbutton.h</header>
<header location="global">ringbutton.h</header>
</customwidget>
</customwidgets>
<resources>
......@@ -2547,7 +2547,7 @@
</connection>
</connections>
<buttongroups>
<buttongroup name="dtmfGroup"/>
<buttongroup name="publishGroup"/>
<buttongroup name="dtmfGroup"/>
</buttongroups>
</ui>
......@@ -54,6 +54,10 @@
#include "imdelegate.h"
#include "pixbufmanipulator.h"
#include "profilemodel.h"
#include "peerprofilecollection.h"
#include "localprofilecollection.h"
CallWidget::CallWidget(QWidget* parent) :
NavWidget(parent),
ui(new Ui::CallWidget),
......@@ -100,6 +104,9 @@ CallWidget::CallWidget(QWidget* parent) :
smartListDelegate_ = new SmartListDelegate();
ui->smartList->setSmartListItemDelegate(smartListDelegate_);
PersonModel::instance().addCollection<PeerProfileCollection>(LoadOptions::FORCE_ENABLED);
ProfileModel::instance().addCollection<LocalProfileCollection>(LoadOptions::FORCE_ENABLED);
PersonModel::instance().
addCollection<WindowsContactBackend>(LoadOptions::FORCE_ENABLED);
......@@ -416,6 +423,7 @@ CallWidget::callIncoming(Call* call)
QApplication::alert(this, 5000);
}
//FIXME: This should update accordingly with profile changes
ui->callerIdLabel->setText(QString(tr("%1", "%1 is the name of the caller"))
.arg(call->formattedName()));
ui->callerPhoto->setPixmap(
......@@ -584,8 +592,10 @@ CallWidget::smartListSelectionChanged(const QItemSelection& newSel, const QItemS
Q_UNUSED(oldSel)
if (newSel.indexes().empty())
if (newSel.indexes().empty()) {
setActualCall(nullptr);
return ui->stackedWidget->setCurrentWidget(ui->welcomePage);
}
auto newIdx = newSel.indexes().first();
if (not newIdx.isValid())
......
......@@ -43,6 +43,7 @@
#include "settingskey.h"
#include "utils.h"
#include "pathpassworddialog.h"
#include "photoboothdialog.h"
#include "accountmodel.h"
#include "protocolmodel.h"
......@@ -50,6 +51,9 @@
#include "callmodel.h"
#include "ringtonemodel.h"
#include "categorizedhistorymodel.h"
#include "profilemodel.h"
#include "profile.h"
#include "person.h"
#ifdef ENABLE_AUTOUPDATE
#include "winsparkle.h"
......@@ -176,6 +180,10 @@ ConfigurationWidget::ConfigurationWidget(QWidget *parent) :
ui->updateDayLabel->hide();
ui->autoUpdateCheckBox->hide();
#endif
auto profile = ProfileModel::instance().selectedProfile();
ui->avatarButton->setIcon(QPixmap::fromImage(Utils::getCirclePhoto(profile->person()->photo().value<QImage>(), ui->avatarButton->width())));
ui->profileNameEdit->setText(profile->person()->formattedName());
}
void ConfigurationWidget::showPreview()
......@@ -390,3 +398,24 @@ ConfigurationWidget::on_exportButton_clicked()
QtConcurrent::run(func, dlg.path_, dlg.password_);
}
}
void
ConfigurationWidget::on_avatarButton_clicked()
{
PhotoBoothDialog dlg;
dlg.exec();
if (dlg.result() == QDialog::Accepted) {
auto image = QImage(dlg.fileName_);
auto avatar = image.scaled(100, 100, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
ProfileModel::instance().selectedProfile()->person()->setPhoto(avatar);
ProfileModel::instance().selectedProfile()->save();
ui->avatarButton->setIcon(QPixmap::fromImage(Utils::getCirclePhoto(avatar, ui->avatarButton->width())));
}
}
void
ConfigurationWidget::on_profileNameEdit_textEdited(const QString& name)
{
ProfileModel::instance().selectedProfile()->person()->setFormattedName(name);
ProfileModel::instance().selectedProfile()->save();
}
......@@ -62,6 +62,8 @@ private slots:
void on_recordPath_clicked();
void on_importButton_clicked();
void on_exportButton_clicked();
void on_avatarButton_clicked();
void on_profileNameEdit_textEdited(const QString& name);
private slots:
void accountSelected(QItemSelection itemSel);
......
......@@ -237,6 +237,84 @@
<property name="spacing">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="label_9">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Profile</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="avatarButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>74</width>
<height>74</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>74</width>
<height>74</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="ressources.qrc">
<normaloff>:/images/user/btn-default-userpic.svg</normaloff>:/images/user/btn-default-userpic.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>74</width>
<height>74</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="profileNameEdit">
<property name="placeholderText">
<string>Name</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_10">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label">
<property name="font">
......@@ -407,7 +485,7 @@
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<height>20</height>
</size>
</property>
</spacer>
......
/***************************************************************************
* Copyright (C) 2016 by Savoir-faire Linux *
* Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
**************************************************************************/
#include "photoboothdialog.h"
#include "ui_photoboothdialog.h"
#include <QFileDialog>
#include "video/previewmanager.h"
PhotoBoothDialog::PhotoBoothDialog(QWidget* parent) :
QDialog(parent),
fileName_("profile.png"),
ui(new Ui::PhotoBoothDialog)
{
ui->setupUi(this);
Qt::WindowFlags flags = windowFlags();
flags = flags & (~Qt::WindowContextHelpButtonHint);
setWindowFlags(flags);
ui->videoFeed->setIsFullPreview(true);
ui->videoFeed->setPhotoMode(true);
Video::PreviewManager::instance().startPreview();
}
PhotoBoothDialog::~PhotoBoothDialog()
{
delete ui;
}
void
PhotoBoothDialog::closeEvent(QCloseEvent* event)
{
Q_UNUSED(event)
Video::PreviewManager::instance().stopPreview();
}
void
PhotoBoothDialog::on_importButton_clicked()
{
fileName_ = QFileDialog::getOpenFileName(this, tr("Choose File"),
"",
tr("Files (*)"));
if (fileName_.isEmpty())
fileName_ = QStringLiteral("profile.png");
else {
Video::PreviewManager::instance().stopPreview();
accept();
}
}
void
PhotoBoothDialog::on_takePhotoButton_clicked()
{
auto photo = ui->videoFeed->takePhoto();
Video::PreviewManager::instance().stopPreview();
photo.save(fileName_);
accept();
}
/***************************************************************************
* Copyright (C) 2016 by Savoir-faire Linux *
* Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
**************************************************************************/
#pragma once
#include <QDialog>
namespace Ui {
class PhotoBoothDialog;
}
class PhotoBoothDialog : public QDialog
{
Q_OBJECT
public:
explicit PhotoBoothDialog(QWidget* parent = 0);
~PhotoBoothDialog();
public:
QString fileName_;
protected:
void closeEvent(QCloseEvent* event);
//UI SLOTS
private slots:
void on_importButton_clicked();
void on_takePhotoButton_clicked();
private:
Ui::PhotoBoothDialog* ui;
};
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PhotoBoothDialog</class>
<widget class="QDialog" name="PhotoBoothDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>791</width>
<height>456</height>
</rect>
</property>
<property name="windowTitle">
<string>Photobooth</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0,0">
<item>
<widget class="VideoWidget" name="videoFeed" native="true"/>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QPushButton" name="takePhotoButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="ressources.qrc">
<normaloff>:/images/icons/ic_photo_camera_white_24dp_2x.png</normaloff>:/images/icons/ic_photo_camera_white_24dp_2x.png</iconset>
</property>
<property name="iconSize">
<size>
<width>36</width>
<height>36</height>
</size>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>or</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QPushButton" name="importButton">
<property name="minimumSize">
<size>
<width>90</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>Import</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>VideoWidget</class>
<extends>QWidget</extends>
<header>videowidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="ressources.qrc"/>
</resources>
<connections/>
</ui>
......@@ -18,12 +18,32 @@
#include "pixbufmanipulator.h"
#include <QtCore/QSize>
#include <QtCore/QMetaType>
#include <QSize>
#include <QMetaType>
#include <QImage>
#include <QIODevice>
#include <QByteArray>
#include <QBuffer>
#include <person.h>
#include <call.h>
#include <contactmethod.h>
#include "person.h"
#include "call.h"
#include "contactmethod.h"
#include "profilemodel.h"
#include "profile.h"
#include "utils.h"
#undef interface
/*Namespace Interfaces collide with QBuffer*/
QByteArray QImageToByteArray(QImage image)
{
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "PNG");
return ba;
}
namespace Interfaces {
......@@ -77,9 +97,12 @@ PixbufManipulator::contactPhoto(Person* c, const QSize& size, bool displayPresen
QVariant PixbufManipulator::personPhoto(const QByteArray& data, const QString& type)
{
Q_UNUSED(type);
Q_UNUSED(data);
return QVariant();
QImage avatar;
QByteArray ba = type.toLatin1();
const char* c_str2 = ba.data();
if (avatar.loadFromData(data.fromBase64(data), c_str2))
return Utils::getCirclePhoto(avatar, avatar.size().width());
return fallbackAvatar_;
}
QVariant
......@@ -99,11 +122,14 @@ PixbufManipulator::securityIssueIcon(const QModelIndex& index)
return QVariant();
}
QByteArray
PixbufManipulator::toByteArray(const QVariant& pxm)
{
Q_UNUSED(pxm);
return QByteArray();
auto image = pxm.value<QImage>();
QByteArray ba = QImageToByteArray(image);
return ba;
}
QVariant
......@@ -180,7 +206,9 @@ QVariant PixbufManipulator::decorationRole(const Person* p)
QVariant PixbufManipulator::decorationRole(const Account* acc)
{
Q_UNUSED(acc)
return QVariant();
return Utils::getCirclePhoto(ProfileModel::instance().
selectedProfile()->person()->photo().value<QImage>(),
imgSize_.width());
}
} // namespace Interfaces
......@@ -27,6 +27,8 @@ Q_DECLARE_METATYPE(QImage);
class Person;
QByteArray QImageToByteArray(QImage image);
namespace Interfaces {
class PixbufManipulator : public PixmapManipulatorI {
......
......@@ -37,5 +37,6 @@
<file>images/icons/ic_arrow_tab_next_black_9dp_2x.png</file>
<file>images/icons/ic_arrow_tab_previous_black_9dp_2x.png</file>
<file>images/spikeMask.png</file>
<file>images/icons/ic_photo_camera_white_24dp_2x.png</file>
</qresource>
</RCC>
......@@ -268,7 +268,7 @@ QWidget#messagingPage{
}
QPushButton#wizardButton, QPushButton#playButton, QPushButton#clearHistoryButton, QPushButton#doTransferButton,
QPushButton#checkUpdateButton{
QPushButton#checkUpdateButton, QPushButton#photoButton, QPushButton#takePhotoButton, QPushButton#importButton{
background-color: #3AC0D2;
border: 0px;
color: white;
......@@ -276,12 +276,14 @@ QPushButton#checkUpdateButton{
}
QPushButton#wizardButton:hover, QPushButton#playButton:hover, QPushButton#clearHistoryButton:hover,
QPushButton#doTransferButton:hover{
QPushButton#doTransferButton:hover, QPushButton#photoButton:hover,
QPushButton#takePhotoButton:hover, QPushButton#importButton:hover{
background-color: #4dc6d6;
}
QPushButton#wizardButton:pressed, QPushButton#playButton:pressed, QPushButton#clearHistoryButton:pressed,
QPushButton#doTransferButton:pressed{
QPushButton#doTransferButton:pressed, QPushButton#photoButton:pressed,
QPushButton#takePhotoButton:pressed, QPushButton#importButton:pressed{
background-color: #34acbd;
}
......@@ -508,6 +510,11 @@ QPushButton#upAudioButton, QPushButton#downAudioButton, QPushButton#upVideoButto
background: transparent;
}
QPushButton#avatarButton:hover {
border: 2px solid rgb(77, 77, 77);
border-radius: 37px;
}
QWidget#leftPannel{
background: white;
}
......
......@@ -31,6 +31,7 @@
//Qt
#include <QObject>
#include <QErrorMessage>
#include <QPainter>
bool
Utils::CreateStartupLink()
......@@ -161,7 +162,7 @@ Utils::GetCurrentUserName() {
wchar_t username[UNLEN+1];
DWORD username_len = UNLEN+1;
GetUserName(username, &username_len);
return QString::fromWCharArray(username, username_len);
return QString::fromWCharArray(username, username_len-1);
#else
return QString();
#endif
......@@ -185,3 +186,25 @@ Utils::InvokeMailto(const QString& subject,
}
#endif
}
QImage
Utils::getCirclePhoto(const QImage original, int sizePhoto)
{
QImage target(sizePhoto, sizePhoto, QImage::Format_ARGB32_Premultiplied);
target.fill(Qt::transparent);
QPainter painter(&target);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
painter.setBrush(QBrush(Qt::white));
auto scaledPhoto = original
.scaled(sizePhoto, sizePhoto, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)
.convertToFormat(QImage::Format_ARGB32_Premultiplied);
int margin = 0;
if (scaledPhoto.width() > sizePhoto) {
margin = (scaledPhoto.width() - sizePhoto) / 2;
}
painter.drawEllipse(0, 0, sizePhoto, sizePhoto);
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
painter.drawImage(0, 0, scaledPhoto, margin, 0);
return target;
}
......@@ -29,6 +29,7 @@
#include <string>
#include <QString>
#include <QImage>
class Utils
{
......@@ -42,5 +43,6 @@ public:
static QString GetISODate();
static QString GetCurrentUserName();
static void InvokeMailto(const QString& subject, const QString& body, const QString& attachement = QString());
static QImage getCirclePhoto(const QImage original, int sizePhoto);
};
......@@ -18,6 +18,8 @@
#include "videowidget.h"
#include "utils.h"
VideoWidget::VideoWidget(QWidget* parent) :
QWidget(parent)
, previewRenderer_(nullptr)
......@@ -121,7 +123,11 @@ VideoWidget::paintEvent(QPaintEvent* evt) {
if(resetPreview_) {
auto previewHeight = fullPreview_ ? height() : height() / 4;
auto previewWidth = fullPreview_ ? width() : width() / 4;
auto scaledPreview = previewImage_->scaled(previewWidth, previewHeight, Qt::KeepAspectRatio);
QImage scaledPreview;
if (photoMode_)
scaledPreview = Utils::getCirclePhoto(*previewImage_, previewHeight);
else
scaledPreview = previewImage_->scaled(previewWidth, previewHeight, Qt::KeepAspectRatio);
auto xDiff = (previewWidth - scaledPreview.width()) / 2;
auto yDiff = (previewHeight - scaledPreview.height()) / 2;
auto yPos = fullPreview_ ? yDiff : height() - previewHeight - previewMargin_;
......@@ -131,11 +137,16 @@ VideoWidget::paintEvent(QPaintEvent* evt) {
resetPreview_ = false;
}
auto scaledPreview = previewImage_->scaled(previewGeometry_.width(),
previewGeometry_.height(),
Qt::KeepAspectRatio);
QImage scaledPreview;
if (photoMode_)
scaledPreview = Utils::getCirclePhoto(*previewImage_, previewGeometry_.height());
else
scaledPreview = previewImage_->scaled(previewGeometry_.width(),
previewGeometry_.height(),
Qt::KeepAspectRatio);
previewGeometry_.setWidth(scaledPreview.width());
previewGeometry_.setHeight(scaledPreview.height());