Add application info at the start.

Add file position on logs.
Add Avatar provider to nitifications.
Fix using `source` on EffectImage that shouldn't be used as an image source because this property is for Loader (ImageProviders cannot be used in this case).
This commit is contained in:
Julien Wadel 2024-03-12 15:47:40 +01:00
parent fb4ee6b579
commit f7749a30e3
23 changed files with 92 additions and 49 deletions

View file

@ -66,6 +66,11 @@ App::App(int &argc, char *argv[])
: SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) { : SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) {
mLinphoneThread = new Thread(this); mLinphoneThread = new Thread(this);
init(); init();
qInfo() << QStringLiteral("Starting application " APPLICATION_NAME " (bin: " EXECUTABLE_NAME
"). Version:%1 Os:%2 Qt:%3")
.arg(applicationVersion())
.arg(Utils::getOsProduct())
.arg(qVersion());
} }
App::~App() { App::~App() {

View file

@ -67,13 +67,32 @@ QtLogger *QtLogger::getInstance() {
return &gLogger; return &gLogger;
} }
QString QtLogger::formatLog(QString contextFile, int contextLine, QString msg) {
QString message;
#ifdef QT_MESSAGELOGCONTEXT
{
QStringList cleanFiles = contextFile.split(Constants::SrcPattern);
QString fileToDisplay = cleanFiles.back();
message = QStringLiteral("%1:%2: ").arg(fileToDisplay).arg(contextLine);
}
#else
Q_UNUSED(contextFile)
Q_UNUSED(contextLine)
#endif
message += msg;
return message;
}
void QtLogger::onQtLog(QtMsgType type, const QMessageLogContext &context, const QString &msg) { void QtLogger::onQtLog(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
QString out; QString out;
QString message = QtLogger::formatLog(context.file, context.line, msg);
if (gLogger.mVerboseEnabled) { if (gLogger.mVerboseEnabled) {
gLogger.printLog(&out, Constants::AppDomain, LinphoneEnums::toLinphone(type), gLogger.printLog(&out, Constants::AppDomain, LinphoneEnums::toLinphone(type),
Utils::appStringToCoreString(msg)); Utils::appStringToCoreString(message));
} }
emit gLogger.qtLogReceived(type, context.file, context.line, msg); emit gLogger.qtLogReceived(type, message);
} }
void QtLogger::enableVerbose(bool verbose) { void QtLogger::enableVerbose(bool verbose) {

View file

@ -48,6 +48,7 @@ public:
static void enableVerbose(bool verbose); static void enableVerbose(bool verbose);
static void enableQtOnly(bool qtOnly); static void enableQtOnly(bool qtOnly);
static QString formatLog(QString contextFile, int contextLine, QString msg);
void printLog(QString *qMessage, const std::string &domain, linphone::LogLevel level, const std::string &message); void printLog(QString *qMessage, const std::string &domain, linphone::LogLevel level, const std::string &message);
// Log Sources // Log Sources
@ -55,7 +56,7 @@ public:
void onLinphoneLog(const std::string &domain, linphone::LogLevel level, const std::string &message); void onLinphoneLog(const std::string &domain, linphone::LogLevel level, const std::string &message);
bool mVerboseEnabled = false; bool mVerboseEnabled = false;
signals: signals:
void qtLogReceived(QtMsgType type, QString contextFile, int contextLine, QString msg); void qtLogReceived(QtMsgType type, QString msg);
void requestVerboseEnabled(bool verbose); void requestVerboseEnabled(bool verbose);
void requestQtOnlyEnabled(bool qtOnly); void requestQtOnlyEnabled(bool qtOnly);
}; };

View file

@ -34,6 +34,7 @@
#include "core/App.hpp" #include "core/App.hpp"
#include "core/call/CallGui.hpp" #include "core/call/CallGui.hpp"
#include "tool/LinphoneEnums.hpp" #include "tool/LinphoneEnums.hpp"
#include "tool/providers/AvatarProvider.hpp"
#include "tool/providers/ImageProvider.hpp" #include "tool/providers/ImageProvider.hpp"
DEFINE_ABSTRACT_OBJECT(Notifier) DEFINE_ABSTRACT_OBJECT(Notifier)
@ -159,6 +160,7 @@ QObject *Notifier::createNotification(Notifier::NotificationType type, QVariantM
// auto engine = App::getInstance()->mEngine; // auto engine = App::getInstance()->mEngine;
auto engine = new QQmlApplicationEngine(); auto engine = new QQmlApplicationEngine();
engine->addImageProvider(ImageProvider::ProviderId, new ImageProvider()); engine->addImageProvider(ImageProvider::ProviderId, new ImageProvider());
engine->addImageProvider(AvatarProvider::ProviderId, new AvatarProvider());
engine->addImportPath(":/"); engine->addImportPath(":/");
// if(showAsTool) window->setProperty("showAsTool",true); // if(showAsTool) window->setProperty("showAsTool",true);
engine->setInitialProperties(data); engine->setInitialProperties(data);

View file

@ -79,22 +79,10 @@ void LoggerModel::enableQtOnly(const bool &enable) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Called from Qt // Called from Qt
void LoggerModel::onQtLog(QtMsgType type, QString contextFile, int contextLine, QString msg) { void LoggerModel::onQtLog(QtMsgType type, QString msg) {
auto service = linphone::LoggingService::get(); auto service = linphone::LoggingService::get();
QString contextStr = "";
#ifdef QT_MESSAGELOGCONTEXT
{
QStringList cleanFiles = contextFile.split(Constants::SrcPattern);
QString fileToDisplay = cleanFiles.back();
contextStr = QStringLiteral("%1:%2: ").arg(fileToDisplay).arg(contextLine); auto serviceMsg = Utils::appStringToCoreString(msg);
}
#else
Q_UNUSED(contextFile)
Q_UNUSED(contextLine)
#endif
auto serviceMsg = Utils::appStringToCoreString(contextStr + msg);
if (service) { if (service) {
switch (type) { switch (type) {
case QtDebugMsg: case QtDebugMsg:

View file

@ -45,7 +45,7 @@ public:
void init(); void init();
void init(const std::shared_ptr<linphone::Config> &config); void init(const std::shared_ptr<linphone::Config> &config);
void onQtLog(QtMsgType type, QString file, int contextLine, QString msg); // Received from Qt void onQtLog(QtMsgType type, QString msg); // Received from Qt
void onLinphoneLog(const std::shared_ptr<linphone::LoggingService> &, void onLinphoneLog(const std::shared_ptr<linphone::LoggingService> &,
const std::string &domain, const std::string &domain,
linphone::LogLevel level, linphone::LogLevel level,

View file

@ -315,3 +315,28 @@ int Utils::getRandomIndex(int size) {
void Utils::copyToClipboard(const QString &text) { void Utils::copyToClipboard(const QString &text) {
QApplication::clipboard()->setText(text); QApplication::clipboard()->setText(text);
} }
QString Utils::getApplicationProduct() {
// Note: Keep '-' as a separator between application name and application type
return QString(APPLICATION_NAME "-Desktop").remove(' ') + "/" + QCoreApplication::applicationVersion();
}
QString Utils::getOsProduct() {
QString version =
QSysInfo::productVersion().remove(' '); // A version can be "Server 2016" (for Windows Server 2016)
QString product = QSysInfo::productType().replace(' ', '-'); // Just in case
return product + "/" + version;
}
QString Utils::computeUserAgent() {
// Placeholder
return "Linphone 6.0";
/*
const std::shared_ptr<linphone::Config> &config
return QStringLiteral("%1 (%2) %3 Qt/%4 LinphoneSDK")
.arg(Utils::getApplicationProduct())
.arg(SettingsModel::getDeviceName(config).replace('\\', "\\\\").replace('(', "\\(").replace(')', "\\)"))
.arg(Utils::getOsProduct())
.arg(qVersion());
*/
}

View file

@ -57,7 +57,7 @@ public:
Q_INVOKABLE static QString getInitials(const QString &username); // Support UTF32 Q_INVOKABLE static QString getInitials(const QString &username); // Support UTF32
Q_INVOKABLE static VariantObject *createCall(const QString &sipAddress, Q_INVOKABLE static VariantObject *createCall(const QString &sipAddress,
bool withVideo = false, bool withVideo = false,
const QString &prepareTransfertAddress = "", const QString &prepareTransfertAddress = "",
const QHash<QString, QString> &headers = {}); const QHash<QString, QString> &headers = {});
Q_INVOKABLE static void openCallsWindow(CallGui *call); Q_INVOKABLE static void openCallsWindow(CallGui *call);
@ -77,6 +77,10 @@ public:
static QString generateSavedFilename(const QString &from, const QString &to); static QString generateSavedFilename(const QString &from, const QString &to);
Q_INVOKABLE static QString generateLinphoneSipAddress(const QString &uri); Q_INVOKABLE static QString generateLinphoneSipAddress(const QString &uri);
static QString getApplicationProduct();
static QString getOsProduct();
static QString computeUserAgent();
static inline QString coreStringToAppString(const std::string &str) { static inline QString coreStringToAppString(const std::string &str) {
if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str); if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str);
else else

View file

@ -210,7 +210,7 @@ Window {
id: callStatusIcon id: callStatusIcon
width: 15 * DefaultStyle.dp width: 15 * DefaultStyle.dp
height: 15 * DefaultStyle.dp height: 15 * DefaultStyle.dp
source:(mainWindow.call.core.state === LinphoneEnums.CallState.End imageSource:(mainWindow.call.core.state === LinphoneEnums.CallState.End
|| mainWindow.call.core.state === LinphoneEnums.CallState.Released) || mainWindow.call.core.state === LinphoneEnums.CallState.Released)
? AppIcons.endCall ? AppIcons.endCall
: (mainWindow.call.core.state === LinphoneEnums.CallState.Paused : (mainWindow.call.core.state === LinphoneEnums.CallState.Paused
@ -264,7 +264,7 @@ Window {
text: mainWindow.call.core.recording ? qsTr("Vous enregistrez l'appel") : qsTr("Votre correspondant enregistre l'appel") text: mainWindow.call.core.recording ? qsTr("Vous enregistrez l'appel") : qsTr("Votre correspondant enregistre l'appel")
} }
EffectImage { EffectImage {
source: AppIcons.recordFill imageSource: AppIcons.recordFill
colorizationColor: DefaultStyle.danger_500main colorizationColor: DefaultStyle.danger_500main
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp
@ -640,7 +640,7 @@ Window {
background: Item {} background: Item {}
contentItem: RowLayout { contentItem: RowLayout {
EffectImage { EffectImage {
source: AppIcons.endCall imageSource: AppIcons.endCall
colorizationColor: DefaultStyle.danger_500main colorizationColor: DefaultStyle.danger_500main
width: 32 * DefaultStyle.dp width: 32 * DefaultStyle.dp
height: 32 * DefaultStyle.dp height: 32 * DefaultStyle.dp
@ -694,7 +694,7 @@ Window {
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
EffectImage { EffectImage {
source: AppIcons.speaker imageSource: AppIcons.speaker
colorizationColor: DefaultStyle.main1_500_main colorizationColor: DefaultStyle.main1_500_main
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp
@ -727,7 +727,7 @@ Window {
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
EffectImage { EffectImage {
source: AppIcons.microphone imageSource: AppIcons.microphone
colorizationColor: DefaultStyle.main1_500_main colorizationColor: DefaultStyle.main1_500_main
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp
@ -795,7 +795,7 @@ Window {
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
EffectImage { EffectImage {
source: AppIcons.videoCamera imageSource: AppIcons.videoCamera
colorizationColor: DefaultStyle.main1_500_main colorizationColor: DefaultStyle.main1_500_main
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp
@ -974,7 +974,7 @@ Window {
} }
contentItem: Item { contentItem: Item {
EffectImage { EffectImage {
source: AppIcons.more imageSource: AppIcons.more
width: 24 * DefaultStyle.dp width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp height: 24 * DefaultStyle.dp
anchors.centerIn: parent anchors.centerIn: parent
@ -1040,7 +1040,7 @@ Window {
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: AppIcons.recordFill imageSource: AppIcons.recordFill
colorizationColor: mainWindow.call.core.recording ? DefaultStyle.danger_500main : undefined colorizationColor: mainWindow.call.core.recording ? DefaultStyle.danger_500main : undefined
} }
Text { Text {
@ -1063,7 +1063,7 @@ Window {
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: AppIcons.recordFill imageSource: AppIcons.recordFill
colorizationColor: mainWindow.call.core.recording ? DefaultStyle.danger_500main : undefined colorizationColor: mainWindow.call.core.recording ? DefaultStyle.danger_500main : undefined
} }
Text { Text {

View file

@ -46,7 +46,7 @@ Item {
contentItem: RowLayout { contentItem: RowLayout {
spacing: 15 * DefaultStyle.dp spacing: 15 * DefaultStyle.dp
EffectImage { EffectImage {
source: AppIcons.smiley imageSource: AppIcons.smiley
colorizationColor: DefaultStyle.success_500main colorizationColor: DefaultStyle.success_500main
Layout.preferredWidth: 32 * DefaultStyle.dp Layout.preferredWidth: 32 * DefaultStyle.dp
Layout.preferredHeight: 32 * DefaultStyle.dp Layout.preferredHeight: 32 * DefaultStyle.dp

View file

@ -70,7 +70,7 @@ Item {
spacing: 5 * DefaultStyle.dp spacing: 5 * DefaultStyle.dp
EffectImage { EffectImage {
id: newAccount id: newAccount
source: AppIcons.plusCircle imageSource: AppIcons.plusCircle
width: 32 * DefaultStyle.dp width: 32 * DefaultStyle.dp
height: 32 * DefaultStyle.dp height: 32 * DefaultStyle.dp
Layout.preferredWidth: 32 * DefaultStyle.dp Layout.preferredWidth: 32 * DefaultStyle.dp

View file

@ -94,7 +94,7 @@ Control.Button {
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
source: mainItem.icon.source imageSource: mainItem.icon.source
imageWidth: mainItem.icon.width imageWidth: mainItem.icon.width
imageHeight: mainItem.icon.height imageHeight: mainItem.icon.height
colorizationColor: mainItem.contentImageColor colorizationColor: mainItem.contentImageColor

View file

@ -16,7 +16,7 @@ Control.CheckBox {
// color: mainItem.checked ? DefaultStyle.main1_500_main : "transparent" // color: mainItem.checked ? DefaultStyle.main1_500_main : "transparent"
EffectImage { EffectImage {
visible: mainItem.checked visible: mainItem.checked
source: AppIcons.check imageSource: AppIcons.check
colorizationColor: DefaultStyle.main1_500_main colorizationColor: DefaultStyle.main1_500_main
anchors.fill: parent anchors.fill: parent
} }

View file

@ -132,7 +132,7 @@ Rectangle{
} }
EffectImage { EffectImage {
id: manageAccount id: manageAccount
source: AppIcons.manageProfile imageSource: AppIcons.manageProfile
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter

View file

@ -132,7 +132,7 @@ ListView {
background: Item{} background: Item{}
contentItem: RowLayout { contentItem: RowLayout {
EffectImage { EffectImage {
source: AppIcons.trashCan imageSource: AppIcons.trashCan
width: 24 * DefaultStyle.dp width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp

View file

@ -10,7 +10,7 @@ import Linphone
Loader { Loader {
id: mainItem id: mainItem
active: visible active: visible
property var source property var imageSource
property var fillMode: Image.PreserveAspectFit property var fillMode: Image.PreserveAspectFit
property var colorizationColor property var colorizationColor
property int imageWidth: width property int imageWidth: width
@ -20,7 +20,7 @@ Loader {
Image { Image {
id: image id: image
visible: !effect2.enabled visible: !effect2.enabled
source: mainItem.source ? mainItem.source : "" source: mainItem.imageSource ? mainItem.imageSource : ""
fillMode: mainItem.fillMode fillMode: mainItem.fillMode
sourceSize.width: width sourceSize.width: width
sourceSize.height: height sourceSize.height: height

View file

@ -1,4 +1,4 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls as Control import QtQuick.Controls as Control
import Linphone import Linphone

View file

@ -22,7 +22,7 @@ MouseArea {
Layout.preferredHeight: mainItem.iconSize Layout.preferredHeight: mainItem.iconSize
width: mainItem.iconSize width: mainItem.iconSize
height: mainItem.iconSize height: mainItem.iconSize
source: mainItem.iconSource imageSource: mainItem.iconSource
colorizationColor: mainItem.color colorizationColor: mainItem.color
} }
Text { Text {

View file

@ -73,7 +73,7 @@ Notification {
Layout.preferredHeight: 40 * DefaultStyle.dp Layout.preferredHeight: 40 * DefaultStyle.dp
contentItem: EffectImage { contentItem: EffectImage {
colorizationColor: DefaultStyle.grey_0 colorizationColor: DefaultStyle.grey_0
source: AppIcons.phone imageSource: AppIcons.phone
imageWidth: 24 * DefaultStyle.dp imageWidth: 24 * DefaultStyle.dp
imageHeight: 24 * DefaultStyle.dp imageHeight: 24 * DefaultStyle.dp
} }
@ -88,7 +88,7 @@ Notification {
Layout.preferredHeight: 40 * DefaultStyle.dp Layout.preferredHeight: 40 * DefaultStyle.dp
contentItem: EffectImage { contentItem: EffectImage {
colorizationColor: DefaultStyle.grey_0 colorizationColor: DefaultStyle.grey_0
source: AppIcons.videoCamera imageSource: AppIcons.videoCamera
imageWidth: 24 * DefaultStyle.dp imageWidth: 24 * DefaultStyle.dp
imageHeight: 24 * DefaultStyle.dp imageHeight: 24 * DefaultStyle.dp
} }
@ -109,7 +109,7 @@ Notification {
Layout.preferredHeight: 40 * DefaultStyle.dp Layout.preferredHeight: 40 * DefaultStyle.dp
contentItem: EffectImage { contentItem: EffectImage {
colorizationColor: DefaultStyle.grey_0 colorizationColor: DefaultStyle.grey_0
source: AppIcons.endCall imageSource: AppIcons.endCall
imageWidth: 24 * DefaultStyle.dp imageWidth: 24 * DefaultStyle.dp
imageHeight: 24 * DefaultStyle.dp imageHeight: 24 * DefaultStyle.dp
} }

View file

@ -1,6 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Controls as Control import QtQuick.Controls as Control
import QtQuick.Layouts 1.0 import QtQuick.Layouts
import Linphone import Linphone
ColumnLayout { ColumnLayout {
@ -118,8 +118,7 @@ ColumnLayout {
icon.source: eyeButton.checked ? AppIcons.eyeShow : AppIcons.eyeHide icon.source: eyeButton.checked ? AppIcons.eyeShow : AppIcons.eyeHide
width: 20 * DefaultStyle.dp width: 20 * DefaultStyle.dp
height: 20 * DefaultStyle.dp height: 20 * DefaultStyle.dp
anchors.top: parent.top anchors.verticalCenter: parent.verticalCenter
anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 15 * DefaultStyle.dp anchors.rightMargin: 15 * DefaultStyle.dp
} }

View file

@ -65,7 +65,7 @@ Control.TabBar {
EffectImage { EffectImage {
id: buttonIcon id: buttonIcon
property int buttonSize: 24 * DefaultStyle.dp property int buttonSize: 24 * DefaultStyle.dp
source: mainItem.currentIndex === index ? modelData.selectedIcon : modelData.icon imageSource: mainItem.currentIndex === index ? modelData.selectedIcon : modelData.icon
Layout.preferredWidth: buttonSize Layout.preferredWidth: buttonSize
Layout.preferredHeight: buttonSize Layout.preferredHeight: buttonSize
width: buttonSize width: buttonSize

View file

@ -198,7 +198,7 @@ Item {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
EffectImage { EffectImage {
colorizationColor: DefaultStyle.grey_0 colorizationColor: DefaultStyle.grey_0
source: mainItem.newItemIconSource imageSource: mainItem.newItemIconSource
width: 24 * DefaultStyle.dp width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp height: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit

View file

@ -82,7 +82,7 @@ AbstractMainPage {
background: Item{} background: Item{}
contentItem: RowLayout { contentItem: RowLayout {
EffectImage { EffectImage {
source: AppIcons.trashCan imageSource: AppIcons.trashCan
width: 24 * DefaultStyle.dp width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp
@ -533,7 +533,7 @@ AbstractMainPage {
property string iconSource property string iconSource
property color colorizationColor: DefaultStyle.main2_500main property color colorizationColor: DefaultStyle.main2_500main
EffectImage { EffectImage {
source: iconLabel.iconSource imageSource: iconLabel.iconSource
width: 24 * DefaultStyle.dp width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp