diff --git a/briar-desktop/build.gradle.kts b/briar-desktop/build.gradle.kts index 7dd010d2197471a3cfe48fdb7d16889207d3f16f..895a312416a298dd16db6c885820bbd4eb079f8b 100644 --- a/briar-desktop/build.gradle.kts +++ b/briar-desktop/build.gradle.kts @@ -35,9 +35,11 @@ val versionCode = "0.3.1" val buildType = if (project.hasProperty("buildType")) project.properties["buildType"] else "snapshot" group = "app.briar.desktop" version = "$versionCode-$buildType" +val windowsAumiName = "Briar" buildData { packageName = "org.briarproject.briar.desktop" + windowsAumi = windowsAumiName } dependencies { @@ -53,9 +55,10 @@ dependencies { implementation(project(path = ":briar-core", configuration = "default")) implementation(project(path = ":bramble-java", configuration = "default")) - implementation("io.github.microutils:kotlin-logging-jvm:2.1.21") - implementation("org.slf4j:jul-to-slf4j:1.7.36") - implementation("ch.qos.logback:logback-classic:1.2.11") + implementation("io.github.microutils:kotlin-logging-jvm:3.0.4") + implementation("org.slf4j:jul-to-slf4j:2.0.5") + implementation("ch.qos.logback:logback-classic:1.4.5") + implementation("de.mobanisto:toast4j:0.2.0") val daggerVersion = "2.24" kapt("com.google.dagger:dagger-compiler:$daggerVersion") diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/DesktopCoreModule.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/DesktopCoreModule.kt index c9a278ad372bfd8b01ee008cb6a9aded7e358e71..c69e24bafeb28fb44490cc0e9129a00009bd2fe4 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/DesktopCoreModule.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/DesktopCoreModule.kt @@ -49,6 +49,7 @@ import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule import org.briarproject.bramble.system.DesktopSecureRandomModule import org.briarproject.bramble.system.JavaSystemModule import org.briarproject.bramble.util.OsUtils.isLinux +import org.briarproject.bramble.util.OsUtils.isWindows import org.briarproject.briar.attachment.AttachmentModule import org.briarproject.briar.desktop.attachment.media.ImageCompressor import org.briarproject.briar.desktop.attachment.media.ImageCompressorImpl @@ -56,6 +57,7 @@ import org.briarproject.briar.desktop.notification.SoundNotificationProvider import org.briarproject.briar.desktop.notification.StubNotificationProvider import org.briarproject.briar.desktop.notification.VisualNotificationProvider import org.briarproject.briar.desktop.notification.linux.LibnotifyNotificationProvider +import org.briarproject.briar.desktop.notification.windows.Toast4jNotificationProvider import org.briarproject.briar.desktop.settings.Configuration import org.briarproject.briar.desktop.settings.ConfigurationImpl import org.briarproject.briar.desktop.settings.EncryptedSettings @@ -191,7 +193,11 @@ internal class DesktopCoreModule( @Provides @Singleton internal fun provideVisualNotificationProvider(): VisualNotificationProvider = - if (isLinux()) LibnotifyNotificationProvider else StubNotificationProvider + when { + isLinux() -> LibnotifyNotificationProvider + isWindows() -> Toast4jNotificationProvider + else -> StubNotificationProvider + } @Provides @Singleton diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/windows/Toast4jNotificationProvider.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/windows/Toast4jNotificationProvider.kt new file mode 100644 index 0000000000000000000000000000000000000000..0690e220208a4d3581ade2bda8a146ad71d14540 --- /dev/null +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/notification/windows/Toast4jNotificationProvider.kt @@ -0,0 +1,88 @@ +/* + * Briar Desktop + * Copyright (C) 2021-2022 The Briar Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package org.briarproject.briar.desktop.notification.windows + +import de.mobanisto.toast4j.ToastBuilder +import de.mobanisto.toast4j.ToastHandle +import de.mobanisto.toast4j.Toaster +import de.mobanisto.wintoast.WinToastTemplate.WinToastTemplateType +import mu.KotlinLogging +import org.briarproject.briar.desktop.BuildData +import org.briarproject.briar.desktop.notification.VisualNotificationProvider +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nF +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nP +import org.briarproject.briar.desktop.utils.KLoggerUtils.e + +object Toast4jNotificationProvider : VisualNotificationProvider { + + private val LOG = KotlinLogging.logger {} + + private lateinit var toaster: Toaster + + override var available: Boolean = false + private set + + private enum class Error { NONE, INIT } + + private var error = Error.NONE + + override val errorMessage: String + get() = when (error) { + Error.INIT -> i18n("settings.notifications.visual.error.toast4j.init") + else -> "" + } + + override fun init() { + toaster = Toaster.forAumi(BuildData.WINDOWS_AUMI) + available = toaster.initialize() + if (!available) { + error = Error.INIT + LOG.e { "unable to initialize toast4j" } + return + } + } + + override fun uninit() { + currentToast?.hide() + } + + var currentToast: ToastHandle? = null + + private fun sendNotification(text: String) { + currentToast?.hide() + currentToast = toaster.showToast( + ToastBuilder(WinToastTemplateType.ToastText01).setSilent() + .setLine1(text).build() + ) + } + + override fun notifyPrivateMessages(num: Int, contacts: Int) = sendNotification( + if (contacts == 1) + i18nP("notifications.message.private.one_chat", num) + else + i18nF("notifications.message.private.several_chats", num, contacts) + ) + + override fun notifyForumPosts(num: Int, forums: Int) = sendNotification( + if (forums == 1) + i18nP("notifications.message.forum.one_forum", num) + else + i18nF("notifications.message.forum.several_forums", num, forums) + ) +} diff --git a/briar-desktop/src/main/resources/strings/BriarDesktop.properties b/briar-desktop/src/main/resources/strings/BriarDesktop.properties index 3b77ccf202c81b684f1f333309ae938a5299c3e0..0ce9d2ea98168a3a8ee7cb7198ccab277599d181 100644 --- a/briar-desktop/src/main/resources/strings/BriarDesktop.properties +++ b/briar-desktop/src/main/resources/strings/BriarDesktop.properties @@ -331,6 +331,7 @@ settings.notifications.visual.title=Visual notifications settings.notifications.visual.error.unsupported=Visual notifications are currently not supported on your system. You can still enable sound notifications. settings.notifications.visual.error.libnotify.load=Notifications can only be shown visually if libnotify is available. Please consider installing it following the usual installation procedure for your system. settings.notifications.visual.error.libnotify.init=Briar Desktop could not connect to any notification server. Please make sure to have a freedesktop.org-compliant notification server installed and configured properly. +settings.notifications.visual.error.toast4j.init=Briar Desktop could not initialize the notification system. settings.notifications.sound.title=Sound notifications # Settings Actions diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/notification/windows/TestNativeNotifications.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/notification/windows/TestNativeNotifications.kt new file mode 100644 index 0000000000000000000000000000000000000000..993f8d3df67499eaa9fc2347f34a1208404922ea --- /dev/null +++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/notification/windows/TestNativeNotifications.kt @@ -0,0 +1,35 @@ +/* + * Briar Desktop + * Copyright (C) 2021-2022 The Briar Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package org.briarproject.briar.desktop.notification.windows + +/** + * Small program to test notification support for Windows. + */ +fun main() { + Toast4jNotificationProvider.apply { + init() + + notifyPrivateMessages(5, 2) + Thread.sleep(3000) + + notifyPrivateMessages(6, 3) + Thread.sleep(3000) + + uninit() + } +} diff --git a/buildSrc/src/main/kotlin/org/briarproject/briar/desktop/builddata/BuildDataPluginExtension.kt b/buildSrc/src/main/kotlin/org/briarproject/briar/desktop/builddata/BuildDataPluginExtension.kt index 81407a40ce1d5227e79dbed4959021a1dfcc8ed2..3fa28ed2a09343b8e8391276a69a22a497680b13 100644 --- a/buildSrc/src/main/kotlin/org/briarproject/briar/desktop/builddata/BuildDataPluginExtension.kt +++ b/buildSrc/src/main/kotlin/org/briarproject/briar/desktop/builddata/BuildDataPluginExtension.kt @@ -28,4 +28,7 @@ open class BuildDataPluginExtension { @Input @Optional var className: String? = null + + @Input + var windowsAumi: String? = null } diff --git a/buildSrc/src/main/kotlin/org/briarproject/briar/desktop/builddata/GenerateBuildDataSourceTask.kt b/buildSrc/src/main/kotlin/org/briarproject/briar/desktop/builddata/GenerateBuildDataSourceTask.kt index 725bed04d51b9b1d103c57111416e7ecc419c6ae..66fe1d44ea5ce20729dd9682fc2f437c5d590cac 100644 --- a/buildSrc/src/main/kotlin/org/briarproject/briar/desktop/builddata/GenerateBuildDataSourceTask.kt +++ b/buildSrc/src/main/kotlin/org/briarproject/briar/desktop/builddata/GenerateBuildDataSourceTask.kt @@ -112,6 +112,10 @@ open class GenerateBuildDataSourceTask : AbstractBuildDataTask() { if (className == null) { className = "BuildData" } + val windowsAumi = configuration?.windowsAumi + + checkNotNull(packageName) { "Please specify 'packageName'." } + checkNotNull(windowsAumi) { "Please specify 'windowsAumi'." } // Get version from Gradle project information val version = project.version.toString() @@ -187,7 +191,6 @@ open class GenerateBuildDataSourceTask : AbstractBuildDataTask() { artifacts.sortWith(compareBy<VersionedArtifact> { it.group }.thenBy { it.artifact }) // Generate output file - checkNotNull(packageName) { "Please specify 'packageName'." } val parts = packageName.split("\\.".toRegex()).toTypedArray() val pathBuildDir = project.buildDir.toPath() val source = Util.getSourceDir(pathBuildDir) @@ -200,7 +203,8 @@ open class GenerateBuildDataSourceTask : AbstractBuildDataTask() { val content = createSource( packageName, className, version, commitTime, gitHash, gitBranch, gitTag, - coreGitHash, coreVersion, artifacts + coreGitHash, coreVersion, artifacts, + windowsAumi ) val input: InputStream = ByteArrayInputStream( content.toByteArray(StandardCharsets.UTF_8) @@ -268,6 +272,7 @@ open class GenerateBuildDataSourceTask : AbstractBuildDataTask() { coreGitHash: String, coreVersion: String, artifacts: List<VersionedArtifact>, + windowsAumi: String, ) = FileBuilder().apply { val branch = if (gitBranch == null) "null" else "\"$gitBranch\"" val tag = if (gitTag == null) "null" else "\"$gitTag\"" @@ -284,6 +289,7 @@ open class GenerateBuildDataSourceTask : AbstractBuildDataTask() { line(" val GIT_TAG: String? = $tag") line(" val CORE_HASH = \"$coreGitHash\"") line(" val CORE_VERSION = \"$coreVersion\"") + line(" val WINDOWS_AUMI = \"$windowsAumi\"") line() line(" val ARTIFACTS: List<Artifact> = buildList {") for (artifact in artifacts) {