Updated TDesktop sources to 2.7.4
This commit is contained in:
		
						commit
						351e7c290d
					
				
					 22 changed files with 349 additions and 270 deletions
				
			
		|  | @ -9,7 +9,7 @@ | |||
|   <Identity Name="TelegramMessengerLLP.TelegramDesktop" | ||||
|     ProcessorArchitecture="ARCHITECTURE" | ||||
|     Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" | ||||
|     Version="2.7.3.0" /> | ||||
|     Version="2.7.4.0" /> | ||||
|   <Properties> | ||||
|     <DisplayName>Telegram Desktop</DisplayName> | ||||
|     <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName> | ||||
|  |  | |||
|  | @ -44,8 +44,8 @@ IDI_ICON1               ICON                    "..\\art\\icon256.ico" | |||
| // | ||||
| 
 | ||||
| VS_VERSION_INFO VERSIONINFO | ||||
|  FILEVERSION 2,7,3,0 | ||||
|  PRODUCTVERSION 2,7,3,0 | ||||
|  FILEVERSION 2,7,4,0 | ||||
|  PRODUCTVERSION 2,7,4,0 | ||||
|  FILEFLAGSMASK 0x3fL | ||||
| #ifdef _DEBUG | ||||
|  FILEFLAGS 0x1L | ||||
|  | @ -62,10 +62,10 @@ BEGIN | |||
|         BEGIN | ||||
|             VALUE "CompanyName", "Telegram FZ-LLC" | ||||
|             VALUE "FileDescription", "Telegram Desktop" | ||||
|             VALUE "FileVersion", "2.7.3.0" | ||||
|             VALUE "FileVersion", "2.7.4.0" | ||||
|             VALUE "LegalCopyright", "Copyright (C) 2014-2021" | ||||
|             VALUE "ProductName", "Telegram Desktop" | ||||
|             VALUE "ProductVersion", "2.7.3.0" | ||||
|             VALUE "ProductVersion", "2.7.4.0" | ||||
|         END | ||||
|     END | ||||
|     BLOCK "VarFileInfo" | ||||
|  |  | |||
|  | @ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US | |||
| // | ||||
| 
 | ||||
| VS_VERSION_INFO VERSIONINFO | ||||
|  FILEVERSION 2,7,3,0 | ||||
|  PRODUCTVERSION 2,7,3,0 | ||||
|  FILEVERSION 2,7,4,0 | ||||
|  PRODUCTVERSION 2,7,4,0 | ||||
|  FILEFLAGSMASK 0x3fL | ||||
| #ifdef _DEBUG | ||||
|  FILEFLAGS 0x1L | ||||
|  | @ -53,10 +53,10 @@ BEGIN | |||
|         BEGIN | ||||
|             VALUE "CompanyName", "Telegram FZ-LLC" | ||||
|             VALUE "FileDescription", "Telegram Desktop Updater" | ||||
|             VALUE "FileVersion", "2.7.3.0" | ||||
|             VALUE "FileVersion", "2.7.4.0" | ||||
|             VALUE "LegalCopyright", "Copyright (C) 2014-2021" | ||||
|             VALUE "ProductName", "Telegram Desktop" | ||||
|             VALUE "ProductVersion", "2.7.3.0" | ||||
|             VALUE "ProductVersion", "2.7.4.0" | ||||
|         END | ||||
|     END | ||||
|     BLOCK "VarFileInfo" | ||||
|  |  | |||
|  | @ -163,14 +163,20 @@ bool BotKeyboard::moderateKeyActivate(int key) { | |||
| 				} | ||||
| 			} else if (const auto user = item->history()->peer->asUser()) { | ||||
| 				if (user->isBot() && item->from() == user) { | ||||
| 					if (key == Qt::Key_Q) { | ||||
| 					if (key == Qt::Key_Q || key == Qt::Key_6) { | ||||
| 						App::sendBotCommand(user, user, qsl("/translate")); | ||||
| 					} else if (key == Qt::Key_W) { | ||||
| 					} else if (key == Qt::Key_W || key == Qt::Key_5) { | ||||
| 						App::sendBotCommand(user, user, qsl("/eng")); | ||||
| 					} else if (key == Qt::Key_3) { | ||||
| 						App::sendBotCommand(user, user, qsl("/pattern")); | ||||
| 					} else if (key == Qt::Key_4) { | ||||
| 						App::sendBotCommand(user, user, qsl("/abuse")); | ||||
| 					} else if (key == Qt::Key_0 || key == Qt::Key_E) { | ||||
| 						App::sendBotCommand(user, user, qsl("/undo")); | ||||
| 					} else if (key == Qt::Key_Plus || key == Qt::Key_QuoteLeft) { | ||||
| 						App::sendBotCommand(user, user, qsl("/next")); | ||||
| 					} else if (key == Qt::Key_Period || key == Qt::Key_S) { | ||||
| 						App::sendBotCommand(user, user, qsl("/stats")); | ||||
| 					} | ||||
| 					return true; | ||||
| 				} | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ constexpr auto AppId = "{C4A4AE8F-B9F7-4CC7-8A6C-BF7EEE87ACA5}"_cs; | |||
| constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; | ||||
| constexpr auto AppName = "Kotatogram Desktop"_cs; | ||||
| constexpr auto AppFile = "Kotatogram"_cs; | ||||
| constexpr auto AppVersion = 2007003; | ||||
| constexpr auto AppVersionStr = "2.7.3"; | ||||
| constexpr auto AppVersion = 2007004; | ||||
| constexpr auto AppVersionStr = "2.7.4"; | ||||
| constexpr auto AppBetaVersion = false; | ||||
| constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; | ||||
|  |  | |||
|  | @ -556,13 +556,8 @@ bool MainWindow::doWeMarkAsRead() { | |||
| 	if (!_main || Ui::isLayerShown()) { | ||||
| 		return false; | ||||
| 	} | ||||
| 	// for tile grid in case other windows have shadows
 | ||||
| 	// i've seen some windows with >70px shadow margins
 | ||||
| 	const auto margin = style::ConvertScale(100); | ||||
| 	return Ui::IsContentVisible( | ||||
| 		this, | ||||
| 		inner().marginsRemoved(QMargins(margin, margin, margin, margin))) | ||||
| 		&& _main->doWeMarkAsRead(); | ||||
| 	updateIsActive(); | ||||
| 	return isActive() && _main->doWeMarkAsRead(); | ||||
| } | ||||
| 
 | ||||
| void MainWindow::checkHistoryActivation() { | ||||
|  |  | |||
|  | @ -8,8 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | |||
| #include "platform/linux/linux_xdp_file_dialog.h" | ||||
| 
 | ||||
| #include "platform/platform_file_utilities.h" | ||||
| #include "platform/linux/linux_desktop_environment.h" | ||||
| #include "platform/linux/specific_linux.h" | ||||
| #include "base/platform/base_platform_info.h" | ||||
| #include "base/platform/linux/base_linux_glibmm_helper.h" | ||||
| #include "storage/localstorage.h" | ||||
|  | @ -642,18 +640,8 @@ rpl::producer<> XDPFileDialog::rejected() { | |||
| } // namespace
 | ||||
| 
 | ||||
| bool Use(Type type) { | ||||
| 	const auto shouldUse = [&] { | ||||
| 		const auto setting = FileDialogType() <= ImplementationType::XDP; | ||||
| 		const auto forceSetting = FileDialogType() == ImplementationType::XDP; | ||||
| 		const auto confined = InFlatpak() || InSnap(); | ||||
| 		const auto notGtkBased = !DesktopEnvironment::IsGtkBased(); | ||||
| 
 | ||||
| 		return setting && (confined || notGtkBased || forceSetting); | ||||
| 	}(); | ||||
| 
 | ||||
| 	static const auto Version = FileChooserPortalVersion(); | ||||
| 
 | ||||
| 	return shouldUse | ||||
| 	return (FileDialogType() <= ImplementationType::XDP) | ||||
| 		&& Version.has_value() | ||||
| 		&& (type != Type::ReadFolder || *Version >= 3); | ||||
| } | ||||
|  |  | |||
|  | @ -715,23 +715,16 @@ void NotificationData::notificationReplied( | |||
| 
 | ||||
| } // namespace
 | ||||
| 
 | ||||
| bool SkipAudio() { | ||||
| 	return Inhibited(); | ||||
| } | ||||
| 
 | ||||
| bool SkipToast() { | ||||
| 	// Do not skip native notifications because of Do not disturb.
 | ||||
| 	// They respect this setting anyway.
 | ||||
| 	if ((Core::App().settings().nativeNotifications() && Supported()) | ||||
| 		|| Enforced()) { | ||||
| bool SkipAudioForCustom() { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| 	return Inhibited(); | ||||
| bool SkipToastForCustom() { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool SkipFlashBounce() { | ||||
| 	return Inhibited(); | ||||
| bool SkipFlashBounceForCustom() { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool Supported() { | ||||
|  | @ -1030,5 +1023,17 @@ void Manager::doClearFromSession(not_null<Main::Session*> session) { | |||
| 	_private->clearFromSession(session); | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipAudio() const { | ||||
| 	return Inhibited(); | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipToast() const { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipFlashBounce() const { | ||||
| 	return Inhibited(); | ||||
| } | ||||
| 
 | ||||
| } // namespace Notifications
 | ||||
| } // namespace Platform
 | ||||
|  |  | |||
|  | @ -34,6 +34,9 @@ protected: | |||
| 	void doClearAllFast() override; | ||||
| 	void doClearFromHistory(not_null<History*> history) override; | ||||
| 	void doClearFromSession(not_null<Main::Session*> session) override; | ||||
| 	bool doSkipAudio() const override; | ||||
| 	bool doSkipToast() const override; | ||||
| 	bool doSkipFlashBounce() const override; | ||||
| 
 | ||||
| private: | ||||
| 	class Private; | ||||
|  |  | |||
|  | @ -32,6 +32,9 @@ protected: | |||
| 	void doClearFromHistory(not_null<History*> history) override; | ||||
| 	void doClearFromSession(not_null<Main::Session*> session) override; | ||||
| 	QString accountNameSeparator() override; | ||||
| 	bool doSkipAudio() const override; | ||||
| 	bool doSkipToast() const override; | ||||
| 	bool doSkipFlashBounce() const override; | ||||
| 
 | ||||
| private: | ||||
| 	class Private; | ||||
|  |  | |||
|  | @ -140,23 +140,16 @@ using Manager = Platform::Notifications::Manager; | |||
| namespace Platform { | ||||
| namespace Notifications { | ||||
| 
 | ||||
| bool SkipAudio() { | ||||
| 	queryDoNotDisturbState(); | ||||
| 	return DoNotDisturbEnabled; | ||||
| } | ||||
| 
 | ||||
| bool SkipToast() { | ||||
| 	if (Supported()) { | ||||
| 		// Do not skip native notifications because of Do not disturb. | ||||
| 		// They respect this setting anyway. | ||||
| bool SkipAudioForCustom() { | ||||
| 	return false; | ||||
| } | ||||
| 	queryDoNotDisturbState(); | ||||
| 	return DoNotDisturbEnabled; | ||||
| 
 | ||||
| bool SkipToastForCustom() { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool SkipFlashBounce() { | ||||
| 	return SkipAudio(); | ||||
| bool SkipFlashBounceForCustom() { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool Supported() { | ||||
|  | @ -438,5 +431,18 @@ QString Manager::accountNameSeparator() { | |||
| 	return QString::fromUtf8(" \xE2\x86\x92 "); | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipAudio() const { | ||||
| 	queryDoNotDisturbState(); | ||||
| 	return DoNotDisturbEnabled; | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipToast() const { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipFlashBounce() const { | ||||
| 	return doSkipAudio(); | ||||
| } | ||||
| 
 | ||||
| } // namespace Notifications | ||||
| } // namespace Platform | ||||
|  |  | |||
|  | @ -12,9 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | |||
| namespace Platform { | ||||
| namespace Notifications { | ||||
| 
 | ||||
| [[nodiscard]] bool SkipAudio(); | ||||
| [[nodiscard]] bool SkipToast(); | ||||
| [[nodiscard]] bool SkipFlashBounce(); | ||||
| [[nodiscard]] bool SkipAudioForCustom(); | ||||
| [[nodiscard]] bool SkipToastForCustom(); | ||||
| [[nodiscard]] bool SkipFlashBounceForCustom(); | ||||
| 
 | ||||
| [[nodiscard]] bool Supported(); | ||||
| [[nodiscard]] bool Enforced(); | ||||
|  |  | |||
|  | @ -257,9 +257,188 @@ void Check() { | |||
| 	InitSucceeded = init(); | ||||
| } | ||||
| 
 | ||||
| bool QuietHoursEnabled = false; | ||||
| DWORD QuietHoursValue = 0; | ||||
| 
 | ||||
| [[nodiscard]] bool UseQuietHoursRegistryEntry() { | ||||
| 	static const bool result = [] { | ||||
| 		// Taken from QSysInfo.
 | ||||
| 		OSVERSIONINFO result = { sizeof(OSVERSIONINFO), 0, 0, 0, 0,{ '\0' } }; | ||||
| 		if (const auto library = GetModuleHandle(L"ntdll.dll")) { | ||||
| 			using RtlGetVersionFunction = NTSTATUS(NTAPI*)(LPOSVERSIONINFO); | ||||
| 			const auto RtlGetVersion = reinterpret_cast<RtlGetVersionFunction>( | ||||
| 				GetProcAddress(library, "RtlGetVersion")); | ||||
| 			if (RtlGetVersion) { | ||||
| 				RtlGetVersion(&result); | ||||
| 			} | ||||
| 		} | ||||
| 		// At build 17134 (Redstone 4) the "Quiet hours" was replaced
 | ||||
| 		// by "Focus assist" and it looks like it doesn't use registry.
 | ||||
| 		return (result.dwMajorVersion == 10 | ||||
| 			&& result.dwMinorVersion == 0 | ||||
| 			&& result.dwBuildNumber < 17134); | ||||
| 	}(); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| // Thanks https://stackoverflow.com/questions/35600128/get-windows-quiet-hours-from-win32-or-c-sharp-api
 | ||||
| void QueryQuietHours() { | ||||
| 	if (!UseQuietHoursRegistryEntry()) { | ||||
| 		// There are quiet hours in Windows starting from Windows 8.1
 | ||||
| 		// But there were several reports about the notifications being shut
 | ||||
| 		// down according to the registry while no quiet hours were enabled.
 | ||||
| 		// So we try this method only starting with Windows 10.
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	LPCWSTR lpKeyName = L"Software\\Microsoft\\Windows\\CurrentVersion\\Notifications\\Settings"; | ||||
| 	LPCWSTR lpValueName = L"NOC_GLOBAL_SETTING_TOASTS_ENABLED"; | ||||
| 	HKEY key; | ||||
| 	auto result = RegOpenKeyEx(HKEY_CURRENT_USER, lpKeyName, 0, KEY_READ, &key); | ||||
| 	if (result != ERROR_SUCCESS) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	DWORD value = 0, type = 0, size = sizeof(value); | ||||
| 	result = RegQueryValueEx(key, lpValueName, 0, &type, (LPBYTE)&value, &size); | ||||
| 	RegCloseKey(key); | ||||
| 
 | ||||
| 	auto quietHoursEnabled = (result == ERROR_SUCCESS) && (value == 0); | ||||
| 	if (QuietHoursEnabled != quietHoursEnabled) { | ||||
| 		QuietHoursEnabled = quietHoursEnabled; | ||||
| 		QuietHoursValue = value; | ||||
| 		LOG(("Quiet hours changed, entry value: %1").arg(value)); | ||||
| 	} else if (QuietHoursValue != value) { | ||||
| 		QuietHoursValue = value; | ||||
| 		LOG(("Quiet hours value changed, was value: %1, entry value: %2").arg(QuietHoursValue).arg(value)); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| bool FocusAssistBlocks = false; | ||||
| 
 | ||||
| // Thanks https://www.withinrafael.com/2019/09/19/determine-if-your-app-is-in-a-focus-assist-profiles-priority-list/
 | ||||
| void QueryFocusAssist() { | ||||
| 	ComPtr<IQuietHoursSettings> quietHoursSettings; | ||||
| 	auto hr = CoCreateInstance( | ||||
| 		CLSID_QuietHoursSettings, | ||||
| 		nullptr, | ||||
| 		CLSCTX_LOCAL_SERVER, | ||||
| 		IID_PPV_ARGS(&quietHoursSettings)); | ||||
| 	if (!SUCCEEDED(hr) || !quietHoursSettings) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	auto profileId = LPWSTR{}; | ||||
| 	const auto guardProfileId = gsl::finally([&] { | ||||
| 		if (profileId) CoTaskMemFree(profileId); | ||||
| 	}); | ||||
| 	hr = quietHoursSettings->get_UserSelectedProfile(&profileId); | ||||
| 	if (!SUCCEEDED(hr) || !profileId) { | ||||
| 		return; | ||||
| 	} | ||||
| 	const auto profileName = QString::fromWCharArray(profileId); | ||||
| 	if (profileName.endsWith(".alarmsonly", Qt::CaseInsensitive)) { | ||||
| 		if (!FocusAssistBlocks) { | ||||
| 			LOG(("Focus Assist: Alarms Only.")); | ||||
| 			FocusAssistBlocks = true; | ||||
| 		} | ||||
| 		return; | ||||
| 	} else if (!profileName.endsWith(".priorityonly", Qt::CaseInsensitive)) { | ||||
| 		if (!profileName.endsWith(".unrestricted", Qt::CaseInsensitive)) { | ||||
| 			LOG(("Focus Assist Warning: Unknown profile '%1'" | ||||
| 				).arg(profileName)); | ||||
| 		} | ||||
| 		if (FocusAssistBlocks) { | ||||
| 			LOG(("Focus Assist: Unrestricted.")); | ||||
| 			FocusAssistBlocks = false; | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
| 	const auto appUserModelId = std::wstring(AppUserModelId::getId()); | ||||
| 	auto blocked = true; | ||||
| 	const auto guard = gsl::finally([&] { | ||||
| 		if (FocusAssistBlocks != blocked) { | ||||
| 			LOG(("Focus Assist: %1, AppUserModelId: %2, Blocks: %3" | ||||
| 				).arg(profileName | ||||
| 				).arg(QString::fromStdWString(appUserModelId) | ||||
| 				).arg(Logs::b(blocked))); | ||||
| 			FocusAssistBlocks = blocked; | ||||
| 		} | ||||
| 	}); | ||||
| 
 | ||||
| 	ComPtr<IQuietHoursProfile> profile; | ||||
| 	hr = quietHoursSettings->GetProfile(profileId, &profile); | ||||
| 	if (!SUCCEEDED(hr) || !profile) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	UINT32 count = 0; | ||||
| 	auto apps = (LPWSTR*)nullptr; | ||||
| 	const auto guardApps = gsl::finally([&] { | ||||
| 		if (apps) CoTaskMemFree(apps); | ||||
| 	}); | ||||
| 	hr = profile->GetAllowedApps(&count, &apps); | ||||
| 	if (!SUCCEEDED(hr) || !apps) { | ||||
| 		return; | ||||
| 	} | ||||
| 	for (UINT32 i = 0; i < count; i++) { | ||||
| 		auto app = apps[i]; | ||||
| 		const auto guardApp = gsl::finally([&] { | ||||
| 			if (app) CoTaskMemFree(app); | ||||
| 		}); | ||||
| 		if (app == appUserModelId) { | ||||
| 			blocked = false; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| QUERY_USER_NOTIFICATION_STATE UserNotificationState = QUNS_ACCEPTS_NOTIFICATIONS; | ||||
| 
 | ||||
| void QueryUserNotificationState() { | ||||
| 	if (Dlls::SHQueryUserNotificationState != nullptr) { | ||||
| 		QUERY_USER_NOTIFICATION_STATE state; | ||||
| 		if (SUCCEEDED(Dlls::SHQueryUserNotificationState(&state))) { | ||||
| 			UserNotificationState = state; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static constexpr auto kQuerySettingsEachMs = 1000; | ||||
| crl::time LastSettingsQueryMs = 0; | ||||
| 
 | ||||
| void QuerySystemNotificationSettings() { | ||||
| 	auto ms = crl::now(); | ||||
| 	if (LastSettingsQueryMs > 0 && ms <= LastSettingsQueryMs + kQuerySettingsEachMs) { | ||||
| 		return; | ||||
| 	} | ||||
| 	LastSettingsQueryMs = ms; | ||||
| 	QueryQuietHours(); | ||||
| 	QueryFocusAssist(); | ||||
| 	QueryUserNotificationState(); | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
| #endif // !__MINGW32__
 | ||||
| 
 | ||||
| bool SkipAudioForCustom() { | ||||
| 	QuerySystemNotificationSettings(); | ||||
| 
 | ||||
| 	return (UserNotificationState == QUNS_NOT_PRESENT) | ||||
| 		|| (UserNotificationState == QUNS_PRESENTATION_MODE) | ||||
| 		|| Global::ScreenIsLocked(); | ||||
| } | ||||
| 
 | ||||
| bool SkipToastForCustom() { | ||||
| 	QuerySystemNotificationSettings(); | ||||
| 
 | ||||
| 	return (UserNotificationState == QUNS_PRESENTATION_MODE) | ||||
| 		|| (UserNotificationState == QUNS_RUNNING_D3D_FULL_SCREEN); | ||||
| } | ||||
| 
 | ||||
| bool SkipFlashBounceForCustom() { | ||||
| 	return SkipToastForCustom(); | ||||
| } | ||||
| 
 | ||||
| bool Supported() { | ||||
| #ifndef __MINGW32__ | ||||
| 	if (!Checked) { | ||||
|  | @ -627,201 +806,23 @@ void Manager::onAfterNotificationActivated( | |||
| 		not_null<Window::SessionController*> window) { | ||||
| 	_private->afterNotificationActivated(id, window); | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipAudio() const { | ||||
| 	return SkipAudioForCustom() | ||||
| 		|| QuietHoursEnabled | ||||
| 		|| FocusAssistBlocks; | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipToast() const { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipFlashBounce() const { | ||||
| 	return SkipFlashBounceForCustom() | ||||
| 		|| QuietHoursEnabled | ||||
| 		|| FocusAssistBlocks; | ||||
| } | ||||
| #endif // !__MINGW32__
 | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| bool QuietHoursEnabled = false; | ||||
| DWORD QuietHoursValue = 0; | ||||
| 
 | ||||
| [[nodiscard]] bool UseQuietHoursRegistryEntry() { | ||||
| 	static const bool result = [] { | ||||
| 		// Taken from QSysInfo.
 | ||||
| 		OSVERSIONINFO result = { sizeof(OSVERSIONINFO), 0, 0, 0, 0,{ '\0' } }; | ||||
| 		if (const auto library = GetModuleHandle(L"ntdll.dll")) { | ||||
| 			using RtlGetVersionFunction = NTSTATUS(NTAPI*)(LPOSVERSIONINFO); | ||||
| 			const auto RtlGetVersion = reinterpret_cast<RtlGetVersionFunction>( | ||||
| 				GetProcAddress(library, "RtlGetVersion")); | ||||
| 			if (RtlGetVersion) { | ||||
| 				RtlGetVersion(&result); | ||||
| 			} | ||||
| 		} | ||||
| 		// At build 17134 (Redstone 4) the "Quiet hours" was replaced
 | ||||
| 		// by "Focus assist" and it looks like it doesn't use registry.
 | ||||
| 		return (result.dwMajorVersion == 10 | ||||
| 			&& result.dwMinorVersion == 0 | ||||
| 			&& result.dwBuildNumber < 17134); | ||||
| 	}(); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| // Thanks https://stackoverflow.com/questions/35600128/get-windows-quiet-hours-from-win32-or-c-sharp-api
 | ||||
| void QueryQuietHours() { | ||||
| 	if (!UseQuietHoursRegistryEntry()) { | ||||
| 		// There are quiet hours in Windows starting from Windows 8.1
 | ||||
| 		// But there were several reports about the notifications being shut
 | ||||
| 		// down according to the registry while no quiet hours were enabled.
 | ||||
| 		// So we try this method only starting with Windows 10.
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	LPCWSTR lpKeyName = L"Software\\Microsoft\\Windows\\CurrentVersion\\Notifications\\Settings"; | ||||
| 	LPCWSTR lpValueName = L"NOC_GLOBAL_SETTING_TOASTS_ENABLED"; | ||||
| 	HKEY key; | ||||
| 	auto result = RegOpenKeyEx(HKEY_CURRENT_USER, lpKeyName, 0, KEY_READ, &key); | ||||
| 	if (result != ERROR_SUCCESS) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	DWORD value = 0, type = 0, size = sizeof(value); | ||||
| 	result = RegQueryValueEx(key, lpValueName, 0, &type, (LPBYTE)&value, &size); | ||||
| 	RegCloseKey(key); | ||||
| 
 | ||||
| 	auto quietHoursEnabled = (result == ERROR_SUCCESS) && (value == 0); | ||||
| 	if (QuietHoursEnabled != quietHoursEnabled) { | ||||
| 		QuietHoursEnabled = quietHoursEnabled; | ||||
| 		QuietHoursValue = value; | ||||
| 		LOG(("Quiet hours changed, entry value: %1").arg(value)); | ||||
| 	} else if (QuietHoursValue != value) { | ||||
| 		QuietHoursValue = value; | ||||
| 		LOG(("Quiet hours value changed, was value: %1, entry value: %2").arg(QuietHoursValue).arg(value)); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| bool FocusAssistBlocks = false; | ||||
| 
 | ||||
| // Thanks https://www.withinrafael.com/2019/09/19/determine-if-your-app-is-in-a-focus-assist-profiles-priority-list/
 | ||||
| void QueryFocusAssist() { | ||||
| 	ComPtr<IQuietHoursSettings> quietHoursSettings; | ||||
| 	auto hr = CoCreateInstance( | ||||
| 		CLSID_QuietHoursSettings, | ||||
| 		nullptr, | ||||
| 		CLSCTX_LOCAL_SERVER, | ||||
| 		IID_PPV_ARGS(&quietHoursSettings)); | ||||
| 	if (!SUCCEEDED(hr) || !quietHoursSettings) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	auto profileId = LPWSTR{}; | ||||
| 	const auto guardProfileId = gsl::finally([&] { | ||||
| 		if (profileId) CoTaskMemFree(profileId); | ||||
| 	}); | ||||
| 	hr = quietHoursSettings->get_UserSelectedProfile(&profileId); | ||||
| 	if (!SUCCEEDED(hr) || !profileId) { | ||||
| 		return; | ||||
| 	} | ||||
| 	const auto profileName = QString::fromWCharArray(profileId); | ||||
| 	if (profileName.endsWith(".alarmsonly", Qt::CaseInsensitive)) { | ||||
| 		if (!FocusAssistBlocks) { | ||||
| 			LOG(("Focus Assist: Alarms Only.")); | ||||
| 			FocusAssistBlocks = true; | ||||
| 		} | ||||
| 		return; | ||||
| 	} else if (!profileName.endsWith(".priorityonly", Qt::CaseInsensitive)) { | ||||
| 		if (!profileName.endsWith(".unrestricted", Qt::CaseInsensitive)) { | ||||
| 			LOG(("Focus Assist Warning: Unknown profile '%1'" | ||||
| 				).arg(profileName)); | ||||
| 		} | ||||
| 		if (FocusAssistBlocks) { | ||||
| 			LOG(("Focus Assist: Unrestricted.")); | ||||
| 			FocusAssistBlocks = false; | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
| 	const auto appUserModelId = std::wstring(AppUserModelId::getId()); | ||||
| 	auto blocked = true; | ||||
| 	const auto guard = gsl::finally([&] { | ||||
| 		if (FocusAssistBlocks != blocked) { | ||||
| 			LOG(("Focus Assist: %1, AppUserModelId: %2, Blocks: %3" | ||||
| 				).arg(profileName | ||||
| 				).arg(QString::fromStdWString(appUserModelId) | ||||
| 				).arg(Logs::b(blocked))); | ||||
| 			FocusAssistBlocks = blocked; | ||||
| 		} | ||||
| 	}); | ||||
| 
 | ||||
| 	ComPtr<IQuietHoursProfile> profile; | ||||
| 	hr = quietHoursSettings->GetProfile(profileId, &profile); | ||||
| 	if (!SUCCEEDED(hr) || !profile) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	UINT32 count = 0; | ||||
| 	auto apps = (LPWSTR*)nullptr; | ||||
| 	const auto guardApps = gsl::finally([&] { | ||||
| 		if (apps) CoTaskMemFree(apps); | ||||
| 	}); | ||||
| 	hr = profile->GetAllowedApps(&count, &apps); | ||||
| 	if (!SUCCEEDED(hr) || !apps) { | ||||
| 		return; | ||||
| 	} | ||||
| 	for (UINT32 i = 0; i < count; i++) { | ||||
| 		auto app = apps[i]; | ||||
| 		const auto guardApp = gsl::finally([&] { | ||||
| 			if (app) CoTaskMemFree(app); | ||||
| 		}); | ||||
| 		if (app == appUserModelId) { | ||||
| 			blocked = false; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| QUERY_USER_NOTIFICATION_STATE UserNotificationState = QUNS_ACCEPTS_NOTIFICATIONS; | ||||
| 
 | ||||
| void QueryUserNotificationState() { | ||||
| 	if (Dlls::SHQueryUserNotificationState != nullptr) { | ||||
| 		QUERY_USER_NOTIFICATION_STATE state; | ||||
| 		if (SUCCEEDED(Dlls::SHQueryUserNotificationState(&state))) { | ||||
| 			UserNotificationState = state; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static constexpr auto kQuerySettingsEachMs = 1000; | ||||
| crl::time LastSettingsQueryMs = 0; | ||||
| 
 | ||||
| void QuerySystemNotificationSettings() { | ||||
| 	auto ms = crl::now(); | ||||
| 	if (LastSettingsQueryMs > 0 && ms <= LastSettingsQueryMs + kQuerySettingsEachMs) { | ||||
| 		return; | ||||
| 	} | ||||
| 	LastSettingsQueryMs = ms; | ||||
| 	QueryQuietHours(); | ||||
| 	QueryFocusAssist(); | ||||
| 	QueryUserNotificationState(); | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
| 
 | ||||
| bool SkipAudio() { | ||||
| 	QuerySystemNotificationSettings(); | ||||
| 
 | ||||
| 	if (UserNotificationState == QUNS_NOT_PRESENT | ||||
| 		|| UserNotificationState == QUNS_PRESENTATION_MODE | ||||
| 		|| QuietHoursEnabled | ||||
| 		|| FocusAssistBlocks | ||||
| 		|| Global::ScreenIsLocked()) { | ||||
| 		return true; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool SkipToast() { | ||||
| 	QuerySystemNotificationSettings(); | ||||
| 
 | ||||
| 	if (UserNotificationState == QUNS_PRESENTATION_MODE | ||||
| 		|| UserNotificationState == QUNS_RUNNING_D3D_FULL_SCREEN | ||||
| 		//|| UserNotificationState == QUNS_BUSY
 | ||||
| 		|| QuietHoursEnabled | ||||
| 		|| FocusAssistBlocks) { | ||||
| 		return true; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool SkipFlashBounce() { | ||||
| 	return SkipToast(); | ||||
| } | ||||
| 
 | ||||
| } // namespace Notifications
 | ||||
| } // namespace Platform
 | ||||
|  |  | |||
|  | @ -39,6 +39,9 @@ protected: | |||
| 	void onAfterNotificationActivated( | ||||
| 		NotificationId id, | ||||
| 		not_null<Window::SessionController*> window) override; | ||||
| 	bool doSkipAudio() const override; | ||||
| 	bool doSkipToast() const override; | ||||
| 	bool doSkipFlashBounce() const override; | ||||
| 
 | ||||
| private: | ||||
| 	class Private; | ||||
|  |  | |||
|  | @ -356,11 +356,35 @@ QRect SeparatePanel::innerGeometry() const { | |||
| 
 | ||||
| void SeparatePanel::initGeometry(QSize size) { | ||||
| 	const auto active = QApplication::activeWindow(); | ||||
| 	const auto center = !active | ||||
| 		? QGuiApplication::primaryScreen()->geometry().center() | ||||
| 		: (active->isVisible() && active->isActiveWindow()) | ||||
| 		? active->geometry().center() | ||||
| 		: active->windowHandle()->screen()->geometry().center(); | ||||
| 	const auto available = !active | ||||
| 		? QGuiApplication::primaryScreen()->availableGeometry() | ||||
| 		: active->windowHandle()->screen()->availableGeometry(); | ||||
| 	const auto parentGeometry = (active | ||||
| 		&& active->isVisible() | ||||
| 		&& active->isActiveWindow()) | ||||
| 		? active->geometry() | ||||
| 		: available; | ||||
| 
 | ||||
| 	auto center = parentGeometry.center(); | ||||
| 	if (size.height() > available.height()) { | ||||
| 		size = QSize(size.width(), available.height()); | ||||
| 	} | ||||
| 	if (center.x() + size.width() / 2 | ||||
| 		> available.x() + available.width()) { | ||||
| 		center.setX( | ||||
| 			available.x() + available.width() - size.width() / 2); | ||||
| 	} | ||||
| 	if (center.x() - size.width() / 2 < available.x()) { | ||||
| 		center.setX(available.x() + size.width() / 2); | ||||
| 	} | ||||
| 	if (center.y() + size.height() / 2 | ||||
| 		> available.y() + available.height()) { | ||||
| 		center.setY( | ||||
| 			available.y() + available.height() - size.height() / 2); | ||||
| 	} | ||||
| 	if (center.y() - size.height() / 2 < available.y()) { | ||||
| 		center.setY(available.y() + size.height() / 2); | ||||
| 	} | ||||
| 	_useTransparency = Ui::Platform::TranslucentWindowsSupported(center); | ||||
| 	_padding = _useTransparency | ||||
| 		? st::callShadow.extend | ||||
|  |  | |||
|  | @ -140,6 +140,8 @@ System::SkipState System::skipNotification( | |||
| } | ||||
| 
 | ||||
| void System::schedule(not_null<HistoryItem*> item) { | ||||
| 	Expects(_manager != nullptr); | ||||
| 
 | ||||
| 	const auto history = item->history(); | ||||
| 	const auto skip = skipNotification(item); | ||||
| 	if (skip.value == SkipState::Skip) { | ||||
|  | @ -169,7 +171,7 @@ void System::schedule(not_null<HistoryItem*> item) { | |||
| 		_whenAlerts[history].emplace(when, notifyBy); | ||||
| 	} | ||||
| 	if (Core::App().settings().desktopNotify() | ||||
| 		&& !Platform::Notifications::SkipToast()) { | ||||
| 		&& !_manager->skipToast()) { | ||||
| 		auto &whenMap = _whenMaps[history]; | ||||
| 		if (whenMap.find(item->id) == whenMap.end()) { | ||||
| 			whenMap.emplace(item->id, when); | ||||
|  | @ -393,7 +395,7 @@ void System::showNext() { | |||
| 	} | ||||
| 	const auto &settings = Core::App().settings(); | ||||
| 	if (alert) { | ||||
| 		if (settings.flashBounceNotify() && !Platform::Notifications::SkipFlashBounce()) { | ||||
| 		if (settings.flashBounceNotify() && !_manager->skipFlashBounce()) { | ||||
| 			if (const auto window = Core::App().activeWindow()) { | ||||
| 				if (const auto handle = window->widget()->windowHandle()) { | ||||
| 					handle->alert(kSystemAlertDuration); | ||||
|  | @ -401,7 +403,7 @@ void System::showNext() { | |||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if (settings.soundNotify() && !Platform::Notifications::SkipAudio()) { | ||||
| 		if (settings.soundNotify() && !_manager->skipAudio()) { | ||||
| 			ensureSoundCreated(); | ||||
| 			_soundTrack->playOnce(); | ||||
| 			Media::Player::mixer()->suppressAll(_soundTrack->getLengthMs()); | ||||
|  | @ -409,7 +411,7 @@ void System::showNext() { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (_waiters.empty() || !settings.desktopNotify() || Platform::Notifications::SkipToast()) { | ||||
| 	if (_waiters.empty() || !settings.desktopNotify() || _manager->skipToast()) { | ||||
| 		if (nextAlert) { | ||||
| 			_waitTimer.callOnce(nextAlert - ms); | ||||
| 		} | ||||
|  |  | |||
|  | @ -202,6 +202,16 @@ public: | |||
| 
 | ||||
| 	[[nodiscard]] virtual ManagerType type() const = 0; | ||||
| 
 | ||||
| 	[[nodiscard]] bool skipAudio() const { | ||||
| 		return doSkipAudio(); | ||||
| 	} | ||||
| 	[[nodiscard]] bool skipToast() const { | ||||
| 		return doSkipToast(); | ||||
| 	} | ||||
| 	[[nodiscard]] bool skipFlashBounce() const { | ||||
| 		return doSkipFlashBounce(); | ||||
| 	} | ||||
| 
 | ||||
| 	virtual ~Manager() = default; | ||||
| 
 | ||||
| protected: | ||||
|  | @ -218,6 +228,9 @@ protected: | |||
| 	virtual void doClearFromItem(not_null<HistoryItem*> item) = 0; | ||||
| 	virtual void doClearFromHistory(not_null<History*> history) = 0; | ||||
| 	virtual void doClearFromSession(not_null<Main::Session*> session) = 0; | ||||
| 	virtual bool doSkipAudio() const = 0; | ||||
| 	virtual bool doSkipToast() const = 0; | ||||
| 	virtual bool doSkipFlashBounce() const = 0; | ||||
| 	[[nodiscard]] virtual bool forceHideDetails() const { | ||||
| 		return false; | ||||
| 	} | ||||
|  | @ -298,6 +311,15 @@ protected: | |||
| 	} | ||||
| 	void doClearFromSession(not_null<Main::Session*> session) override { | ||||
| 	} | ||||
| 	bool doSkipAudio() const { | ||||
| 		return false; | ||||
| 	} | ||||
| 	bool doSkipToast() const { | ||||
| 		return false; | ||||
| 	} | ||||
| 	bool doSkipFlashBounce() const { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -404,6 +404,18 @@ void Manager::doClearFromItem(not_null<HistoryItem*> item) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipAudio() const { | ||||
| 	return Platform::Notifications::SkipAudioForCustom(); | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipToast() const { | ||||
| 	return Platform::Notifications::SkipToastForCustom(); | ||||
| } | ||||
| 
 | ||||
| bool Manager::doSkipFlashBounce() const { | ||||
| 	return Platform::Notifications::SkipFlashBounceForCustom(); | ||||
| } | ||||
| 
 | ||||
| void Manager::doUpdateAll() { | ||||
| 	for_const (auto ¬ification, _notifications) { | ||||
| 		notification->updateNotifyDisplay(); | ||||
|  |  | |||
|  | @ -76,6 +76,9 @@ private: | |||
| 	void doClearFromHistory(not_null<History*> history) override; | ||||
| 	void doClearFromSession(not_null<Main::Session*> session) override; | ||||
| 	void doClearFromItem(not_null<HistoryItem*> item) override; | ||||
| 	bool doSkipAudio() const; | ||||
| 	bool doSkipToast() const; | ||||
| 	bool doSkipFlashBounce() const; | ||||
| 
 | ||||
| 	void showNextFromQueue(); | ||||
| 	void unlinkFromShown(Notification *remove); | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| AppVersion         2007003 | ||||
| AppVersion         2007004 | ||||
| AppVersionStrMajor 2.7 | ||||
| AppVersionStrSmall 2.7.3 | ||||
| AppVersionStr      2.7.3 | ||||
| AppVersionStrSmall 2.7.4 | ||||
| AppVersionStr      2.7.4 | ||||
| BetaChannel        0 | ||||
| AlphaVersion       0 | ||||
| AppVersionOriginal 2.7.3 | ||||
| AppVersionOriginal 2.7.4 | ||||
|  |  | |||
|  | @ -1,3 +1,9 @@ | |||
| 2.7.4 (28.04.21) | ||||
| 
 | ||||
| - Fix crash in viewing an invoice after a payment is made. | ||||
| - Respect Focus Assist only for native notifications. | ||||
| - Mark messages as read only in active window. | ||||
| 
 | ||||
| 2.7.3 (27.04.21) | ||||
| 
 | ||||
| - Fix crash on some versions of Linux. | ||||
|  |  | |||
|  | @ -148,7 +148,7 @@ parts: | |||
|     prime: [-./*] | ||||
| 
 | ||||
|   desktop-qt5: | ||||
|     source: https://github.com/ubuntu/snapcraft-desktop-helpers.git | ||||
|     source: https://github.com/desktop-app/snapcraft-desktop-helpers.git | ||||
|     source-subdir: qt | ||||
|     plugin: make | ||||
|     make-parameters: ["FLAVOR=qt5"] | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue