diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5cfbb56..3839048 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: cleanmeter - path: target\server\build\compose\binaries\main\app + path: target\desktop\build\compose\binaries\main\app dependency-submission: diff --git a/README.md b/README.md index 3e3226e..a7c5050 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,33 @@ -# Clean Meter - -> Did you like my work? Consider supporting me so I can keep putting hours in. -> - [ko-fi](https://ko-fi.com/danil0v3s) -> - [PayPal](https://www.paypal.com/donate/?hosted_button_id=W2GU6AHGQUND8) - -> [!IMPORTANT] -> We are preparing our first major release 1.0.0. It's already available in [beta1](https://github.com/Danil0v3s/CleanMeter/releases/tag/1.0.0.beta-1) and it makes our app completely standalone, without the need of HWiNFO or MSI Afterburner anymore. Try it out and let us know! +Payload headless CMS Admin panel built with React +
+
+ + +

+ GitHub Workflow Status +   + Discord +   + npm +   + GitHub Release +

+
+

+Donate via ko-fi · Donate via PayPal · Follow our Designer +

+
> [!IMPORTANT] -> We released 0.0.4 which DROPPED the need of MSI Afterburner and rely entirely on HWInfo alone. We heard some of you dislike Afterburner so we opted to go the least friction route. +> 🎉 We've released 1.0! Star this repo or keep an eye on it to follow along. -## What is this? +CleanMeter is the first gamer oriented clean statistics tracker, no fuss no muss, dead simple setup and good looking visuals. -A small desktop companion app to display sleek graphs overlays from HWInfo. Designs were made by [/u/Violetmars](https://www.reddit.com/user/Violetmars/) - - - - -## Getting started -1. If you have HWiNFO installed already, make sure that `Enable Shared Memory Support` is **enabled** at the settings. -![image](images/qa_clean_meter.png) -> [!IMPORTANT] -> If you chose to have HWiNFO installed, _**you**_ will have to manage the `Shared Memory Support` limitation of 12 hours. Every 12 hours of uptime you will need to re-enable the setting. It seems the timer resets if you shutdown your computer before 12h uptime. -2. If you didn't have HWiNFO installed, there's no need to do any additional setup. (We manage a portable version of it ourselves) -3. Download the latest release, run it +### Benefits over traditional performance trackers +- This looks good +- No fluorescent numbers with Arial 12 +- Doesn't mess with your power profile or fan setup -## Current Limitations +### Current Limitations - Doesn't work with exclusive fullscreen +- Given our mission is to look _good_ and _clean_ at the same time, we might hold on to release new features because we need time to think how to accommodate our UI. We can't just enable every possible sensor, sorry. diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/PreferencesRepository.kt b/core/native/src/main/kotlin/app/cleanmeter/core/os/PreferencesRepository.kt similarity index 63% rename from target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/PreferencesRepository.kt rename to core/native/src/main/kotlin/app/cleanmeter/core/os/PreferencesRepository.kt index b9573f3..d6f8c67 100644 --- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/PreferencesRepository.kt +++ b/core/native/src/main/kotlin/app/cleanmeter/core/os/PreferencesRepository.kt @@ -1,8 +1,5 @@ -package app.cleanmeter.target.desktop.data +package app.cleanmeter.core.os -import app.cleanmeter.target.desktop.model.OverlaySettings -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json import java.util.prefs.Preferences const val OVERLAY_SETTINGS_PREFERENCE_KEY = "OVERLAY_SETTINGS_PREFERENCE_KEY" @@ -26,18 +23,4 @@ object PreferencesRepository { fun setPreference(key: String, value: String) = prefs.put(key, value) fun setPreferenceBoolean(key: String, value: Boolean) = prefs.putBoolean(key, value) fun clear() = prefs.clear() -} - -fun PreferencesRepository.loadOverlaySettings(): OverlaySettings { - val json = getPreferenceString(OVERLAY_SETTINGS_PREFERENCE_KEY) - val settings = if (json != null) { - try { - Json.decodeFromString(json) - } catch (e: Exception) { - OverlaySettings() - } - } else { - OverlaySettings() - } - return settings } \ No newline at end of file diff --git a/core/native/src/main/kotlin/app/cleanmeter/core/os/hardwaremonitor/HardwareMonitorProcessManager.kt b/core/native/src/main/kotlin/app/cleanmeter/core/os/hardwaremonitor/HardwareMonitorProcessManager.kt index 3d5bb6e..f788663 100644 --- a/core/native/src/main/kotlin/app/cleanmeter/core/os/hardwaremonitor/HardwareMonitorProcessManager.kt +++ b/core/native/src/main/kotlin/app/cleanmeter/core/os/hardwaremonitor/HardwareMonitorProcessManager.kt @@ -4,16 +4,53 @@ import app.cleanmeter.core.os.util.isDev import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.IOException import java.nio.file.Path import java.util.* object HardwareMonitorProcessManager { private var process: Process? = null - fun checkRuntime() { - ProcessBuilder().apply { - command("dotnet --list-runtimes") - }.start() + suspend fun checkRuntime(): Boolean { + return try { + val process = ProcessBuilder().apply { + command("cmd.exe", "/c", "dotnet", "--list-runtimes") + }.start() + + val scannerIn = Scanner(process.inputStream) + val scannerErr = Scanner(process.errorStream) + + val stdOutput = emptyList().toMutableList() + val errOutput = emptyList().toMutableList() + + CoroutineScope(Dispatchers.IO).launch { + try { + while (scannerIn.hasNextLine()) { + stdOutput.add(scannerIn.nextLine()) + } + } catch (e: Exception) { + return@launch + } + + } + CoroutineScope(Dispatchers.IO).launch { + try { + while (scannerErr.hasNextLine()) { + errOutput.add(scannerIn.nextLine()) + } + } catch (e: Exception) { + return@launch + } + } + + return withContext(Dispatchers.IO) { + val exitCode = process.waitFor() + exitCode == 0 && errOutput.isEmpty() + } + } catch (ex: Exception) { + return false + } } fun start() { @@ -32,12 +69,12 @@ object HardwareMonitorProcessManager { val scannerErr = Scanner(process!!.errorStream) CoroutineScope(Dispatchers.IO).launch { - while(scannerIn.hasNextLine()) { + while (scannerIn.hasNextLine()) { System.out.println(scannerIn.nextLine()) } } CoroutineScope(Dispatchers.IO).launch { - while(scannerErr.hasNextLine()) { + while (scannerErr.hasNextLine()) { System.err.println(scannerErr.nextLine()) } } @@ -57,7 +94,8 @@ object HardwareMonitorProcessManager { val command = listOf( "cmd.exe", "/c", - "sc create svcleanmeter displayname= \"CleanMeter Service\" binPath= $file start= auto group= LocalServiceNoNetworkFirewall") + "sc create svcleanmeter displayname= \"CleanMeter Service\" binPath= $file start= auto group= LocalServiceNoNetworkFirewall" + ) ProcessBuilder().apply { command(command) }.start() diff --git a/core/native/src/main/kotlin/app/cleanmeter/core/os/hardwaremonitor/SocketClient.kt b/core/native/src/main/kotlin/app/cleanmeter/core/os/hardwaremonitor/SocketClient.kt index b99f577..ab76fa4 100644 --- a/core/native/src/main/kotlin/app/cleanmeter/core/os/hardwaremonitor/SocketClient.kt +++ b/core/native/src/main/kotlin/app/cleanmeter/core/os/hardwaremonitor/SocketClient.kt @@ -1,5 +1,7 @@ package app.cleanmeter.core.os.hardwaremonitor +import app.cleanmeter.core.os.PREFERENCE_PERMISSION_CONSENT +import app.cleanmeter.core.os.PreferencesRepository import app.cleanmeter.core.os.util.getByteBuffer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -55,7 +57,9 @@ object SocketClient { val packetFlow: Flow = packetChannel.receiveAsFlow() init { - connect() + if (PreferencesRepository.getPreferenceBoolean(PREFERENCE_PERMISSION_CONSENT, false)) { + connect() + } } private fun connect() = CoroutineScope(Dispatchers.IO).launch { diff --git a/core/native/src/main/kotlin/app/cleanmeter/core/os/win32/WindowsService.kt b/core/native/src/main/kotlin/app/cleanmeter/core/os/win32/WindowsService.kt index 960b98e..babf2af 100644 --- a/core/native/src/main/kotlin/app/cleanmeter/core/os/win32/WindowsService.kt +++ b/core/native/src/main/kotlin/app/cleanmeter/core/os/win32/WindowsService.kt @@ -78,10 +78,14 @@ class WindowsService { fun tryElevateProcess(isAutostart: Boolean) { if (isAutostart) return if (!isDev() && !WindowsService.isProcessElevated()) { - val currentDir = Path.of("").toAbsolutePath().toString() - Shell32Impl.INSTANCE.ShellExecuteW(null, "runas", "$currentDir\\cleanmeter.exe", "", "", 10) - exitProcess(0) + elevateProcess() } } + + fun elevateProcess() { + val currentDir = Path.of("").toAbsolutePath().toString() + Shell32Impl.INSTANCE.ShellExecuteW(null, "runas", "$currentDir\\cleanmeter.exe", "", "", 10) + exitProcess(0) + } } } diff --git a/images/Logo.png b/images/Logo.png deleted file mode 100644 index c4a114c..0000000 Binary files a/images/Logo.png and /dev/null differ diff --git a/images/compose-logo.svg b/images/compose-logo.svg deleted file mode 100644 index 1aa3f8b..0000000 --- a/images/compose-logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/images/qa_clean_meter.png b/images/qa_clean_meter.png deleted file mode 100644 index 16b5b7d..0000000 Binary files a/images/qa_clean_meter.png and /dev/null differ diff --git a/images/qa_clean_meter2.png b/images/qa_clean_meter2.png deleted file mode 100644 index 023a0ff..0000000 Binary files a/images/qa_clean_meter2.png and /dev/null differ diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/DesktopMain.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/DesktopMain.kt index 966e887..a2808d2 100644 --- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/DesktopMain.kt +++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/DesktopMain.kt @@ -5,8 +5,8 @@ import app.cleanmeter.core.common.reporting.ApplicationParams import app.cleanmeter.core.os.ProcessManager import app.cleanmeter.core.os.util.isDev import app.cleanmeter.core.os.win32.WindowsService -import app.cleanmeter.target.desktop.data.PREFERENCE_PERMISSION_CONSENT -import app.cleanmeter.target.desktop.data.PreferencesRepository +import app.cleanmeter.core.os.PREFERENCE_PERMISSION_CONSENT +import app.cleanmeter.core.os.PreferencesRepository fun main(vararg args: String) = singleInstance(args) { if (PreferencesRepository.getPreferenceBoolean(PREFERENCE_PERMISSION_CONSENT, false)) { diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/OverlaySettingsRepository.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/OverlaySettingsRepository.kt index 25adcd8..7dda900 100644 --- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/OverlaySettingsRepository.kt +++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/OverlaySettingsRepository.kt @@ -1,5 +1,7 @@ package app.cleanmeter.target.desktop.data +import app.cleanmeter.core.os.OVERLAY_SETTINGS_PREFERENCE_KEY +import app.cleanmeter.core.os.PreferencesRepository import app.cleanmeter.target.desktop.model.OverlaySettings import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/PreferenceRepositoryExtensions.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/PreferenceRepositoryExtensions.kt new file mode 100644 index 0000000..0f429d3 --- /dev/null +++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/PreferenceRepositoryExtensions.kt @@ -0,0 +1,21 @@ +package app.cleanmeter.target.desktop.data + +import app.cleanmeter.core.os.OVERLAY_SETTINGS_PREFERENCE_KEY +import app.cleanmeter.core.os.PreferencesRepository +import app.cleanmeter.target.desktop.model.OverlaySettings +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json + +fun PreferencesRepository.loadOverlaySettings(): OverlaySettings { + val json = getPreferenceString(OVERLAY_SETTINGS_PREFERENCE_KEY) + val settings = if (json != null) { + try { + Json.decodeFromString(json) + } catch (e: Exception) { + OverlaySettings() + } + } else { + OverlaySettings() + } + return settings +} \ No newline at end of file diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/Progress.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/Progress.kt index 25ec621..fc547d6 100644 --- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/Progress.kt +++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/Progress.kt @@ -38,7 +38,7 @@ fun Progress( val color = when { value in 0f..boundaries.low.div(100f) -> Green value in boundaries.low.div(100f)..boundaries.medium.div(100f) -> Yellow - value > boundaries.high.div(100f) -> Red + value > boundaries.medium.div(100f) -> Red else -> White } @@ -87,43 +87,59 @@ fun Progress( @Preview @Composable private fun ProgressPreview() { + Column { + ProgressRow(OverlaySettings.ProgressType.Bar) + ProgressRow(OverlaySettings.ProgressType.Circular) + } +} + +@Composable +private fun ProgressRow(progressType :OverlaySettings.ProgressType) { Row(modifier = Modifier.background(Color.Black)) { Progress( - value = 0.5f, - label = "05", - progressType = OverlaySettings.ProgressType.Bar, + value = 0.12f, + label = "12", + progressType = progressType, + unit = "C", + boundaries = OverlaySettings.Sensor.GraphSensor.Boundaries() + ) + + Progress( + value = 0.53f, + label = "53", + progressType = progressType, unit = "C", boundaries = OverlaySettings.Sensor.GraphSensor.Boundaries() ) Progress( - value = 0.6f, - label = "06", - progressType = OverlaySettings.ProgressType.Bar, + value = 0.67f, + label = "67", + progressType = progressType, unit = "C", boundaries = OverlaySettings.Sensor.GraphSensor.Boundaries() ) Progress( - value = 0.7f, - label = "07", - progressType = OverlaySettings.ProgressType.Bar, + value = 0.72f, + label = "72", + progressType = progressType, unit = "C", boundaries = OverlaySettings.Sensor.GraphSensor.Boundaries() ) Progress( - value = 0.8f, - label = "08", - progressType = OverlaySettings.ProgressType.Bar, + value = 0.86f, + label = "86", + progressType = progressType, unit = "C", boundaries = OverlaySettings.Sensor.GraphSensor.Boundaries() ) Progress( - value = 0.9f, - label = "09", - progressType = OverlaySettings.ProgressType.Bar, + value = 0.99f, + label = "99", + progressType = progressType, unit = "C", boundaries = OverlaySettings.Sensor.GraphSensor.Boundaries() ) diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/RuntimeToast.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/RuntimeToast.kt new file mode 100644 index 0000000..32f2960 --- /dev/null +++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/RuntimeToast.kt @@ -0,0 +1,135 @@ +package app.cleanmeter.target.desktop.ui.components + +import ClearButton +import FilledButton +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Icon +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Warning +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.unit.dp +import app.cleanmeter.core.designsystem.LocalColorScheme +import app.cleanmeter.core.designsystem.LocalTypography +import app.cleanmeter.target.desktop.ui.AppTheme +import kotlinx.coroutines.delay +import kotlin.system.exitProcess + +@Composable +internal fun BoxScope.RuntimeToast() { + val uriHandler = LocalUriHandler.current + val density = LocalDensity.current + var visible by remember { + mutableStateOf(false) + } + + LaunchedEffect(Unit) { + delay(1000) + visible = true + } + + AnimatedVisibility( + visible = visible, + enter = slideInVertically { + with(density) { 40.dp.roundToPx() } + }, + exit = slideOutVertically { + with(density) { 40.dp.roundToPx() } + }, + modifier = Modifier.align(Alignment.BottomCenter) + ) { + Row( + modifier = Modifier + .padding(bottom = 16.dp) + .fillMaxWidth(0.95f) + .background(LocalColorScheme.current.background.brand, RoundedCornerShape(100)) + .padding(horizontal = 16.dp, vertical = 14.dp) + .align(Alignment.BottomCenter) + .animateContentSize(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Box(modifier = Modifier.size(40.dp), contentAlignment = Alignment.Center) { + Icon( + imageVector = Icons.Rounded.Warning, + contentDescription = null, + tint = LocalColorScheme.current.icon.inverse, + modifier = Modifier.fillMaxSize().background(LocalColorScheme.current.background.brandSubtle, RoundedCornerShape(100)).padding(10.dp), + ) + } + BodyText() + CallToAction( + onCloseClick = { exitProcess(0) }, + onUpdateClick = { + uriHandler.openUri("https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-8.0.11-windows-x64-installer") + exitProcess(0) + }, + ) + } + } +} + +@Composable +private fun RowScope.BodyText() { + Column(modifier = Modifier.weight(1f)) { + Text( + text = ".NET Core Runtime not available", + color = LocalColorScheme.current.text.inverse, + style = LocalTypography.current.labelLMedium, + modifier = Modifier.wrapContentHeight(), + ) + Text( + text = "Please re-open CleanMeter after the installation.", + color = LocalColorScheme.current.text.disabled, + style = LocalTypography.current.labelLMedium, + modifier = Modifier.wrapContentHeight(), + ) + } +} + + + +@Composable +private fun RowScope.CallToAction( + onCloseClick: () -> Unit, + onUpdateClick: () -> Unit, +) { + ClearButton(label = "Later", onClick = onCloseClick) + FilledButton(label = "Install now", onClick = onUpdateClick) +} + +@Preview +@Composable +private fun RuntimeToastPreview() { + AppTheme(false) { + Box(modifier = Modifier.fillMaxSize().padding(8.dp)) { + RuntimeToast() + } + } +} \ No newline at end of file diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/TopBar.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/TopBar.kt index 3410698..a90e710 100644 --- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/TopBar.kt +++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/TopBar.kt @@ -51,7 +51,7 @@ internal fun TopBar( ) { Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) { Image( - painter = painterResource("imgs/favicon.ico"), + painter = painterResource("imgs/logo.svg"), contentDescription = "logo", modifier = Modifier.size(25.dp), ) diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/UpdateToast.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/UpdateToast.kt index 4e254a0..afe011f 100644 --- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/UpdateToast.kt +++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/UpdateToast.kt @@ -166,14 +166,14 @@ private fun IconProgress(state: UpdateState) { is UpdateState.Available -> Icon( painter = painterResource("icons/cloud_download.svg"), contentDescription = null, - tint = LocalColorScheme.current.border.inverse, + tint = LocalColorScheme.current.icon.inverse, modifier = Modifier.fillMaxSize().background(LocalColorScheme.current.background.brandSubtle, RoundedCornerShape(100)).padding(10.dp), ) is UpdateState.Downloaded -> Icon( painter = painterResource("icons/download_done.svg"), contentDescription = null, - tint = LocalColorScheme.current.border.inverse, + tint = LocalColorScheme.current.icon.inverse, modifier = Modifier.fillMaxSize().background(LocalColorScheme.current.background.brandSubtle, RoundedCornerShape(100)).padding(10.dp), ) diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/Settings.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/Settings.kt index 2da003f..a57f57b 100644 --- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/Settings.kt +++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/Settings.kt @@ -41,6 +41,7 @@ import app.cleanmeter.core.common.hardwaremonitor.networkReadings import app.cleanmeter.core.designsystem.LocalColorScheme import app.cleanmeter.core.designsystem.LocalTypography import app.cleanmeter.target.desktop.ui.AppTheme +import app.cleanmeter.target.desktop.ui.components.RuntimeToast import app.cleanmeter.target.desktop.ui.components.SettingsTab import app.cleanmeter.target.desktop.ui.components.TopBar import app.cleanmeter.target.desktop.ui.components.UpdateToast @@ -101,9 +102,12 @@ fun WindowScope.Settings( ) } - if (updaterState !is UpdateState.NotAvailable) { + if (updaterState !is UpdateState.NotAvailable && settingsState.isRuntimeAvailable) { UpdateToast() } + if (!settingsState.isRuntimeAvailable) { + RuntimeToast() + } } } } diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsViewModel.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsViewModel.kt index e9f302b..752481f 100644 --- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsViewModel.kt +++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsViewModel.kt @@ -3,7 +3,7 @@ package app.cleanmeter.target.desktop.ui.settings import androidx.compose.ui.unit.IntOffset import androidx.lifecycle.ViewModel import app.cleanmeter.core.common.hardwaremonitor.HardwareMonitorData -import app.cleanmeter.core.common.reporting.ApplicationParams +import app.cleanmeter.core.os.hardwaremonitor.HardwareMonitorProcessManager import app.cleanmeter.core.os.hardwaremonitor.HardwareMonitorReader import app.cleanmeter.core.os.hardwaremonitor.Packet import app.cleanmeter.core.os.hardwaremonitor.SocketClient @@ -11,8 +11,8 @@ import app.cleanmeter.core.os.win32.WindowsService import app.cleanmeter.target.desktop.KeyboardEvent import app.cleanmeter.target.desktop.KeyboardManager import app.cleanmeter.target.desktop.data.OverlaySettingsRepository -import app.cleanmeter.target.desktop.data.PREFERENCE_PERMISSION_CONSENT -import app.cleanmeter.target.desktop.data.PreferencesRepository +import app.cleanmeter.core.os.PREFERENCE_PERMISSION_CONSENT +import app.cleanmeter.core.os.PreferencesRepository import app.cleanmeter.target.desktop.model.OverlaySettings import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -31,6 +31,7 @@ data class SettingsState( val hardwareData: HardwareMonitorData? = null, val isRecording: Boolean = false, val adminConsent: Boolean = false, + val isRuntimeAvailable: Boolean = false, ) sealed class SettingsEvent { @@ -65,6 +66,7 @@ class SettingsViewModel : ViewModel() { observeRecordingHotkey() observeRecordingState() sendInitialPollingRate() + checkForNetCoreRuntime() _state.update { it.copy( @@ -73,6 +75,13 @@ class SettingsViewModel : ViewModel() { } } + private fun checkForNetCoreRuntime() { + CoroutineScope(Dispatchers.IO).launch { + val isRuntimeAvailable = HardwareMonitorProcessManager.checkRuntime() + _state.update { it.copy(isRuntimeAvailable = isRuntimeAvailable) } + } + } + private fun observeOverlaySettings() { CoroutineScope(Dispatchers.IO).launch { OverlaySettingsRepository @@ -174,7 +183,7 @@ class SettingsViewModel : ViewModel() { private fun onConsentGiven() { PreferencesRepository.setPreferenceBoolean(PREFERENCE_PERMISSION_CONSENT, true) _state.update { it.copy(adminConsent = true) } - WindowsService.tryElevateProcess(ApplicationParams.isAutostart) + WindowsService.elevateProcess() } private fun onBoundarySet( diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsWindow.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsWindow.kt index 4380197..4e78241 100644 --- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsWindow.kt +++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsWindow.kt @@ -15,14 +15,12 @@ import androidx.compose.ui.window.ApplicationScope import androidx.compose.ui.window.Tray import androidx.compose.ui.window.Window import androidx.compose.ui.window.rememberWindowState -import app.cleanmeter.target.desktop.data.PREFERENCE_START_MINIMIZED -import app.cleanmeter.target.desktop.data.PreferencesRepository +import app.cleanmeter.core.os.PREFERENCE_START_MINIMIZED +import app.cleanmeter.core.os.PreferencesRepository import com.github.kwhat.jnativehook.GlobalScreen import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import java.awt.GraphicsEnvironment -import java.awt.event.ComponentAdapter -import java.awt.event.ComponentEvent import kotlin.math.min @Composable @@ -34,11 +32,7 @@ fun ApplicationScope.SettingsWindow( val maximumWindowBounds = remember { GraphicsEnvironment.getLocalGraphicsEnvironment().maximumWindowBounds.height } val minimumHeight = remember { min(800, maximumWindowBounds) } var isVisible by remember { - mutableStateOf( - PreferencesRepository.getPreferenceBooleanNullable( - PREFERENCE_START_MINIMIZED - )?.not() ?: true - ) + mutableStateOf(!PreferencesRepository.getPreferenceBoolean(PREFERENCE_START_MINIMIZED, false)) } val icon = painterResource("imgs/logo.png") val state = rememberWindowState().apply { diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/tabs/AppSettingsUi.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/tabs/AppSettingsUi.kt index a970bfe..32688fd 100644 --- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/tabs/AppSettingsUi.kt +++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/tabs/AppSettingsUi.kt @@ -26,8 +26,8 @@ import androidx.compose.ui.unit.dp import app.cleanmeter.core.designsystem.LocalColorScheme import app.cleanmeter.core.designsystem.LocalTypography import app.cleanmeter.core.os.win32.WinRegistry -import app.cleanmeter.target.desktop.data.PREFERENCE_START_MINIMIZED -import app.cleanmeter.target.desktop.data.PreferencesRepository +import app.cleanmeter.core.os.PREFERENCE_START_MINIMIZED +import app.cleanmeter.core.os.PreferencesRepository import app.cleanmeter.target.desktop.model.OverlaySettings import app.cleanmeter.target.desktop.ui.components.CheckboxWithLabel import app.cleanmeter.target.desktop.ui.components.StyleCard diff --git a/target/desktop/src/main/resources/imgs/favicon.ico b/target/desktop/src/main/resources/imgs/favicon.ico index 71275e6..9d74369 100644 Binary files a/target/desktop/src/main/resources/imgs/favicon.ico and b/target/desktop/src/main/resources/imgs/favicon.ico differ diff --git a/target/desktop/src/main/resources/imgs/logo.png b/target/desktop/src/main/resources/imgs/logo.png index c4a114c..35c6e4b 100644 Binary files a/target/desktop/src/main/resources/imgs/logo.png and b/target/desktop/src/main/resources/imgs/logo.png differ diff --git a/target/desktop/src/main/resources/imgs/logo.svg b/target/desktop/src/main/resources/imgs/logo.svg new file mode 100644 index 0000000..752c9e3 --- /dev/null +++ b/target/desktop/src/main/resources/imgs/logo.svg @@ -0,0 +1,6 @@ + + + + + +