Commit d8b7c705 authored by Andreas Traczyk's avatar Andreas Traczyk Committed by Ming Rui Zhang

packaging: remove old installations

- removes startup links, reg keys, and uninstall links from
  antecedent versions of Jami

Change-Id: If5c4e6c95244560b62f1eec985583cf04b7edd5e
parent c980980c
......@@ -17,6 +17,7 @@
<ComponentRef Id="ApplicationShortcutDesktop" />
<ComponentRef Id="ApplicationShortcutStartMenu" />
</Feature>
<!--Visual C++-->
<DirectoryRef Id="TARGETDIR">
<Merge Id="VCRedist" SourceFile="$(env.VCRedistMergeModule)" DiskId="1" Language="0" />
......@@ -50,6 +51,11 @@
<WixVariable Id="WixUIBannerBmp" Value="top-banner.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="main-banner.bmp" />
<WixVariable Id="WixUISupportPerUser" Value="0" />
<CustomAction Id="removeOldJamiFiles" Directory="APPLICATIONFOLDER"
ExeCommand="cmd /c &quot;del vc_redist.x64.exe; del uninstall.exe;&quot;"
Execute="deferred" Return="ignore" HideTarget="no" Impersonate="no" />
</Product>
<Fragment Id="DirectoryStructure">
......@@ -70,17 +76,19 @@
<Shortcut Id="ApplicationShortcutDesktop" Name="$(var.Name)" Description="Launch $(var.Name)"
Target="[#fileMain.exe]" WorkingDirectory="INSTALLFOLDER" />
<RemoveFolder Id="DesktopFolder" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software/$(var.Name)" Name="desktop" Type="integer" Value="1" KeyPath="yes" />
<RegistryValue Root="HKCU" Key="Software\jami.net\$(var.Name)" Name="desktop" Type="integer" Value="1" KeyPath="yes" />
</Component>
</DirectoryRef>
<DirectoryRef Id="ApplicationProgramsFolder">
<Component Id="ApplicationShortcutStartMenu" Guid="*" Win64="yes">
<Shortcut Id="ApplicationShortcutStartMenu" Name="$(var.Name)" Description="Launch $(var.Name)"
<Component Id="ApplicationShortcutStartMenu" Guid="*" Win64="yes">
<Shortcut Id="ApplicationShortcutStartMenu" Name="$(var.Name)" Description="Launch $(var.Name)"
Target="[#fileMain.exe]" WorkingDirectory="INSTALLFOLDER" />
<RemoveFolder Id="StartMenuFolder" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software/$(var.Name)" Name="startmenu" Type="integer" Value="1" KeyPath="yes" />
</Component>
<RemoveFolder Id="StartMenuFolder" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software\jami.net\$(var.Name)" Name="startmenu" Type="integer" Value="1" KeyPath="yes" />
<RemoveRegistryKey Id='removeBadShortcutRegKey' Root='HKLM' Key='Software/$(var.Name)' Action='removeOnInstall'/>
<RemoveRegistryKey Id='removeOldUninstRegKey' Root='HKLM' Key='SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$(var.Manufacturer) $(var.Name)' Action='removeOnInstall'/>
</Component>
</DirectoryRef>
</Fragment>
......@@ -108,6 +116,7 @@
</InstallUISequence>
</UI>
<InstallExecuteSequence>
<Custom Action="removeOldJamiFiles" After="RemoveFiles" />
<Custom Action="LaunchApplication_nonUI" After="InstallFinalize"> WIXNONUILAUNCH </Custom>
<Custom Action="Overwrite_WixSetDefaultPerMachineFolder" After="WixSetDefaultPerMachineFolder" />
</InstallExecuteSequence>
......
......@@ -132,6 +132,8 @@ main(int argc, char* argv[])
return 0;
}
Utils::removeOldVersions();
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, false);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
......
......@@ -437,7 +437,7 @@ NewWizardWidget::processWizardInformations()
changePage(ui->spinnerPage);
createAccount();
Utils::CreateStartupLink();
Utils::CreateStartupLink(L"Jami");
}
void
......
......@@ -49,7 +49,7 @@
#include "version.h"
bool
Utils::CreateStartupLink()
Utils::CreateStartupLink(const std::wstring& wstrAppName)
{
#ifdef Q_OS_WIN
TCHAR szPath[MAX_PATH];
......@@ -61,7 +61,7 @@ Utils::CreateStartupLink()
SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath);
std::wstring linkPath(startupPath);
linkPath += TEXT("\\Jami.lnk");
linkPath += std::wstring(TEXT("\\") + wstrAppName + TEXT(".lnk"));
return Utils::CreateLink(programPath.c_str(), linkPath.c_str());
#else
......@@ -100,32 +100,81 @@ Utils::CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink) {
}
void
Utils::DeleteStartupLink() {
Utils::DeleteStartupLink(const std::wstring& wstrAppName) {
#ifdef Q_OS_WIN
TCHAR startupPath[MAX_PATH];
SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath);
std::wstring linkPath(startupPath);
linkPath += TEXT("\\Jami.lnk");
linkPath += std::wstring(TEXT("\\") + wstrAppName + TEXT(".lnk"));
DeleteFile(linkPath.c_str());
#endif
}
bool
Utils::CheckStartupLink() {
Utils::CheckStartupLink(const std::wstring& wstrAppName) {
#ifdef Q_OS_WIN
TCHAR startupPath[MAX_PATH];
SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath);
std::wstring linkPath(startupPath);
linkPath += TEXT("\\Jami.lnk");
linkPath += std::wstring(TEXT("\\") + wstrAppName + TEXT(".lnk"));
return PathFileExists(linkPath.c_str());
#else
return true;
#endif
}
const char*
Utils::WinGetEnv(const char* name)
{
#ifdef Q_OS_WIN
const DWORD buffSize = 65535;
static char buffer[buffSize];
if (GetEnvironmentVariableA(name, buffer, buffSize)) {
return buffer;
} else {
return 0;
}
#else
return 0;
#endif
}
void
Utils::removeOldVersions()
{
#ifdef Q_OS_WIN
// As per: https://git.jami.net/savoirfairelinux/ring-client-windows/issues/429
// NB: As only the 64-bit version of this application is distributed, we will only
// remove 1. the configuration reg keys for Ring-x64, 2. the startup links for Ring,
// 3. the winsparkle reg keys. The NSIS uninstall reg keys for Jami-x64 are removed
// by the MSI installer.
// Uninstallation of Ring, either 32 or 64 bit, is left to the user.
// The current version of Jami will attempt to kill Ring.exe upon start if a startup
// link is found.
QString node64 = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node";
QString hkcuSoftwareKey = "HKEY_CURRENT_USER\\Software\\";
QString uninstKey = "\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\";
QString company = "Savoir-Faire Linux";
// 1. configuration reg keys for Ring-x64
QSettings(hkcuSoftwareKey + "jami.net\\Ring", QSettings::NativeFormat).remove("");
QSettings(hkcuSoftwareKey + "ring.cx", QSettings::NativeFormat).remove("");
// 2. unset Ring as a startup application
if (Utils::CheckStartupLink(TEXT("Ring"))) {
qDebug() << "Found startup link for Ring. Removing it and killing Ring.exe.";
Utils::DeleteStartupLink(TEXT("Ring"));
QProcess::execute("taskkill /im Ring.exe /f");
}
// 3. remove registry entries for winsparkle(both Jami-x64 and Ring-x64)
QSettings(hkcuSoftwareKey + company, QSettings::NativeFormat).remove("");
#else
return;
#endif
}
QString
Utils::GetRingtonePath() {
#ifdef Q_OS_WIN
......@@ -236,18 +285,6 @@ void Utils::showSystemNotification(QWidget* widget,
QApplication::alert(widget, delay);
}
const char*
Utils::WinGetEnv(const char* name)
{
const DWORD buffSize = 65535;
static char buffer[buffSize];
if (GetEnvironmentVariableA(name, buffer, buffSize)) {
return buffer;
} else {
return 0;
}
}
void
Utils::cleanUpdateFiles()
{
......
......@@ -52,139 +52,141 @@ static const QSize IMAGE_SIZE{ 128, 128 };
namespace Utils
{
// system
bool CreateStartupLink();
void DeleteStartupLink();
bool CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink);
bool CheckStartupLink();
QString GetRingtonePath();
QString GenGUID();
QString GetISODate();
void InvokeMailto(const QString& subject, const QString& body, const QString& attachement = QString());
void setStackWidget(QStackedWidget *stack, QWidget *widget);
void showSystemNotification(QWidget* widget, const QString& message, long delay = 5000);
void showSystemNotification(QWidget* widget, const QString& sender, const QString& message, long delay = 5000);
const char* WinGetEnv(const char* name);
// updates
void cleanUpdateFiles();
void checkForUpdates(bool withUI, QWidget* parent = nullptr);
void applyUpdates(QWidget* parent = nullptr);
// names
std::string bestIdForConversation(const lrc::api::conversation::Info& conv, const lrc::api::ConversationModel& model);
std::string bestIdForAccount(const lrc::api::account::Info & account);
std::string bestNameForAccount(const lrc::api::account::Info & account);
std::string bestIdForContact(const lrc::api::contact::Info & contact);
std::string bestNameForContact(const lrc::api::contact::Info & contact);
std::string bestNameForConversation(const lrc::api::conversation::Info & conv, const lrc::api::ConversationModel & model);
std::string secondBestNameForAccount(const lrc::api::account::Info& account); // returns empty string if only infoHash is available
lrc::api::profile::Type profileType(const lrc::api::conversation::Info & conv, const lrc::api::ConversationModel & model);
// interactions
std::string formatTimeString(const std::time_t& timestamp);
lrc::api::ConversationModel::ConversationQueue::const_iterator getConversationFromUid(const std::string& uid, const lrc::api::ConversationModel& model);
lrc::api::ConversationModel::ConversationQueue::const_iterator getConversationFromUri(const std::string& uri, const lrc::api::ConversationModel& model);
bool isInteractionGenerated(const lrc::api::interaction::Type& interaction);
bool isContactValid(const std::string& contactUid, const lrc::api::ConversationModel& model);
// image
QImage getCirclePhoto(const QImage original, int sizePhoto);
QImage conversationPhoto(const std::string& convUid, const lrc::api::account::Info& accountInfo);
QColor getAvatarColor(const QString& canonicalUri);
QImage fallbackAvatar(const QSize size, const QString& canonicalUriStr, const QString& letterStr = QString());
QImage fallbackAvatar(const QSize size, const ContactMethod* cm);
QImage fallbackAvatar(const QSize size, const std::string& alias, const std::string& uri);
QByteArray QImageToByteArray(QImage image);
QByteArray QByteArrayFromFile(const QString& filename);
QPixmap generateTintedPixmap(const QString& filename, QColor color);
QImage scaleAndFrame(const QImage photo, const QSize& size = IMAGE_SIZE);
QImage accountPhoto(const lrc::api::account::Info& accountInfo, const QSize& size = IMAGE_SIZE);
QImage cropImage(const QImage& img);
// convo
lrc::api::conversation::Info getConversationFromCallId(const std::string& callId);
lrc::api::conversation::Info getSelectedConversation();
lrc::api::conversation::Info getConversationFromUid(const std::string & convUid, bool filtered = true);
// misc helpers
void swapQListWidgetItems(QListWidget* list, bool down = true);
template <typename Func1, typename Func2>
void
oneShotConnect(const typename QtPrivate::FunctionPointer<Func1>::Object* sender, Func1 signal, Func2 slot)
{
QMetaObject::Connection* const connection = new QMetaObject::Connection;
*connection = QObject::connect(sender, signal, slot);
QMetaObject::Connection* const disconnectConnection = new QMetaObject::Connection;
*disconnectConnection = QObject::connect(sender, signal,
[connection, disconnectConnection] {
if (connection) {
QObject::disconnect(*connection);
delete connection;
}
if (disconnectConnection) {
QObject::disconnect(*disconnectConnection);
delete disconnectConnection;
}
});
}
template <typename Func1, typename Func2>
void
oneShotConnect( const typename QtPrivate::FunctionPointer<Func1>::Object* sender, Func1 signal,
const typename QtPrivate::FunctionPointer<Func2>::Object* receiver, Func2 slot)
{
QMetaObject::Connection* const connection = new QMetaObject::Connection;
*connection = QObject::connect(sender, signal, receiver, slot);
QMetaObject::Connection* const disconnectConnection = new QMetaObject::Connection;
*disconnectConnection = QObject::connect(sender, signal,
[connection, disconnectConnection] {
if (connection) {
QObject::disconnect(*connection);
delete connection;
}
if (disconnectConnection) {
QObject::disconnect(*disconnectConnection);
delete disconnectConnection;
}
});
}
template<typename T>
void
setElidedText(T* object, const QString &text, Qt::TextElideMode mode = Qt::ElideMiddle, int padding = 32) {
QFontMetrics metrics(object->font());
QString clippedText = metrics.elidedText(text, mode, object->width() - padding);
object->setText(clippedText);
}
template<typename E>
constexpr inline typename std::enable_if< std::is_enum<E>::value,
typename std::underlying_type<E>::type
>::type
toUnderlyingValue(E e) noexcept
{
return static_cast<typename std::underlying_type<E>::type >(e);
}
template<typename E, typename T>
constexpr inline typename std::enable_if< std::is_enum<E>::value && std::is_integral<T>::value,
E
>::type
toEnum(T value) noexcept
{
return static_cast<E>(value);
}
// system
bool CreateStartupLink(const std::wstring& wstrAppName);
void DeleteStartupLink(const std::wstring& wstrAppName);
bool CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink);
bool CheckStartupLink(const std::wstring& wstrAppName);
void removeOldVersions();
const char* WinGetEnv(const char* name);
QString GetRingtonePath();
QString GenGUID();
QString GetISODate();
void InvokeMailto(const QString& subject, const QString& body, const QString& attachement = QString());
void setStackWidget(QStackedWidget *stack, QWidget *widget);
void showSystemNotification(QWidget* widget, const QString& message, long delay = 5000);
void showSystemNotification(QWidget* widget, const QString& sender, const QString& message, long delay = 5000);
// updates
void cleanUpdateFiles();
void checkForUpdates(bool withUI, QWidget* parent = nullptr);
void applyUpdates(QWidget* parent = nullptr);
// names
std::string bestIdForConversation(const lrc::api::conversation::Info& conv, const lrc::api::ConversationModel& model);
std::string bestIdForAccount(const lrc::api::account::Info & account);
std::string bestNameForAccount(const lrc::api::account::Info & account);
std::string bestIdForContact(const lrc::api::contact::Info & contact);
std::string bestNameForContact(const lrc::api::contact::Info & contact);
std::string bestNameForConversation(const lrc::api::conversation::Info & conv, const lrc::api::ConversationModel & model);
std::string secondBestNameForAccount(const lrc::api::account::Info& account); // returns empty string if only infoHash is available
lrc::api::profile::Type profileType(const lrc::api::conversation::Info & conv, const lrc::api::ConversationModel & model);
// interactions
std::string formatTimeString(const std::time_t& timestamp);
lrc::api::ConversationModel::ConversationQueue::const_iterator getConversationFromUid(const std::string& uid, const lrc::api::ConversationModel& model);
lrc::api::ConversationModel::ConversationQueue::const_iterator getConversationFromUri(const std::string& uri, const lrc::api::ConversationModel& model);
bool isInteractionGenerated(const lrc::api::interaction::Type& interaction);
bool isContactValid(const std::string& contactUid, const lrc::api::ConversationModel& model);
// image
QImage getCirclePhoto(const QImage original, int sizePhoto);
QImage conversationPhoto(const std::string& convUid, const lrc::api::account::Info& accountInfo);
QColor getAvatarColor(const QString& canonicalUri);
QImage fallbackAvatar(const QSize size, const QString& canonicalUriStr, const QString& letterStr = QString());
QImage fallbackAvatar(const QSize size, const ContactMethod* cm);
QImage fallbackAvatar(const QSize size, const std::string& alias, const std::string& uri);
QByteArray QImageToByteArray(QImage image);
QByteArray QByteArrayFromFile(const QString& filename);
QPixmap generateTintedPixmap(const QString& filename, QColor color);
QImage scaleAndFrame(const QImage photo, const QSize& size = IMAGE_SIZE);
QImage accountPhoto(const lrc::api::account::Info& accountInfo, const QSize& size = IMAGE_SIZE);
QImage cropImage(const QImage& img);
// convo
lrc::api::conversation::Info getConversationFromCallId(const std::string& callId);
lrc::api::conversation::Info getSelectedConversation();
lrc::api::conversation::Info getConversationFromUid(const std::string & convUid, bool filtered = true);
// misc helpers
void swapQListWidgetItems(QListWidget* list, bool down = true);
template <typename Func1, typename Func2>
void
oneShotConnect(const typename QtPrivate::FunctionPointer<Func1>::Object* sender, Func1 signal, Func2 slot)
{
QMetaObject::Connection* const connection = new QMetaObject::Connection;
*connection = QObject::connect(sender, signal, slot);
QMetaObject::Connection* const disconnectConnection = new QMetaObject::Connection;
*disconnectConnection = QObject::connect(sender, signal,
[connection, disconnectConnection] {
if (connection) {
QObject::disconnect(*connection);
delete connection;
}
if (disconnectConnection) {
QObject::disconnect(*disconnectConnection);
delete disconnectConnection;
}
});
}
template <typename Func1, typename Func2>
void
oneShotConnect( const typename QtPrivate::FunctionPointer<Func1>::Object* sender, Func1 signal,
const typename QtPrivate::FunctionPointer<Func2>::Object* receiver, Func2 slot)
{
QMetaObject::Connection* const connection = new QMetaObject::Connection;
*connection = QObject::connect(sender, signal, receiver, slot);
QMetaObject::Connection* const disconnectConnection = new QMetaObject::Connection;
*disconnectConnection = QObject::connect(sender, signal,
[connection, disconnectConnection] {
if (connection) {
QObject::disconnect(*connection);
delete connection;
}
if (disconnectConnection) {
QObject::disconnect(*disconnectConnection);
delete disconnectConnection;
}
});
}
template<typename T>
void
setElidedText(T* object, const QString &text, Qt::TextElideMode mode = Qt::ElideMiddle, int padding = 32) {
QFontMetrics metrics(object->font());
QString clippedText = metrics.elidedText(text, mode, object->width() - padding);
object->setText(clippedText);
}
template<typename E>
constexpr inline typename std::enable_if< std::is_enum<E>::value,
typename std::underlying_type<E>::type
>::type
toUnderlyingValue(E e) noexcept
{
return static_cast<typename std::underlying_type<E>::type >(e);
}
template<typename E, typename T>
constexpr inline typename std::enable_if< std::is_enum<E>::value && std::is_integral<T>::value,
E
>::type
toEnum(T value) noexcept
{
return static_cast<E>(value);
}
template<typename T>
ptrdiff_t
indexInVector(const std::vector<T>& vec, const T& item)
{
auto it = std::find(vec.begin(), vec.end(), item);
if (it == vec.end()) {
return -1;
}
return std::distance(vec.begin(), it);
template<typename T>
ptrdiff_t
indexInVector(const std::vector<T>& vec, const T& item)
{
auto it = std::find(vec.begin(), vec.end(), item);
if (it == vec.end()) {
return -1;
}
return std::distance(vec.begin(), it);
}
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment