Compare commits
2
Commits
8f5d5d1ca8
...
22c0a61e32
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22c0a61e32 | ||
|
|
057be15822 |
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: android desktop android-usb android-emulator
|
.PHONY: android desktop desktop-install android-usb android-emulator
|
||||||
|
|
||||||
ADB := $(shell command -v adb 2>/dev/null || echo "$(HOME)/Library/Android/sdk/platform-tools/adb")
|
ADB := $(shell command -v adb 2>/dev/null || echo "$(HOME)/Library/Android/sdk/platform-tools/adb")
|
||||||
EMULATOR := $(shell command -v emulator 2>/dev/null || echo "$(HOME)/Library/Android/sdk/emulator/emulator")
|
EMULATOR := $(shell command -v emulator 2>/dev/null || echo "$(HOME)/Library/Android/sdk/emulator/emulator")
|
||||||
@@ -11,6 +11,10 @@ android:
|
|||||||
desktop:
|
desktop:
|
||||||
./gradlew composeApp:run
|
./gradlew composeApp:run
|
||||||
|
|
||||||
|
desktop-install:
|
||||||
|
./gradlew composeApp:packageDistributionForCurrentOS
|
||||||
|
cp -R composeApp/build/compose/binaries/main/app/myicek.app /Applications/myicek.app
|
||||||
|
|
||||||
android-usb: android
|
android-usb: android
|
||||||
$(ADB) -d install -r $(APK)
|
$(ADB) -d install -r $(APK)
|
||||||
$(ADB) -d shell am start -n ch.parano.myicek/.MainActivity
|
$(ADB) -d shell am start -n ch.parano.myicek/.MainActivity
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
// MyIce Kotlin Multiplatform — schedule/convocation viewer
|
||||||
|
// Copyright (C) 2026 parano.ch
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package ch.parano.myicek
|
||||||
|
|
||||||
|
import com.russhwolf.settings.Settings
|
||||||
|
|
||||||
|
data class SavedWindowState(
|
||||||
|
val width: Int,
|
||||||
|
val height: Int,
|
||||||
|
val x: Int,
|
||||||
|
val y: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
class WindowStateStorage(private val settings: Settings) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val WIDTH_KEY = "window_width"
|
||||||
|
private const val HEIGHT_KEY = "window_height"
|
||||||
|
private const val X_KEY = "window_x"
|
||||||
|
private const val Y_KEY = "window_y"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save(width: Int, height: Int, x: Int, y: Int) {
|
||||||
|
settings.putInt(WIDTH_KEY, width)
|
||||||
|
settings.putInt(HEIGHT_KEY, height)
|
||||||
|
settings.putInt(X_KEY, x)
|
||||||
|
settings.putInt(Y_KEY, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun load(): SavedWindowState? {
|
||||||
|
val width = settings.getIntOrNull(WIDTH_KEY) ?: return null
|
||||||
|
val height = settings.getIntOrNull(HEIGHT_KEY) ?: return null
|
||||||
|
val x = settings.getIntOrNull(X_KEY) ?: return null
|
||||||
|
val y = settings.getIntOrNull(Y_KEY) ?: return null
|
||||||
|
return SavedWindowState(width, height, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
// MyIce Kotlin Multiplatform — schedule/convocation viewer
|
||||||
|
// Copyright (C) 2026 parano.ch
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package ch.parano.myicek
|
||||||
|
|
||||||
|
import com.russhwolf.settings.MapSettings
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
|
class WindowStateStorageTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun saveAndLoadWindowState() {
|
||||||
|
val settings = MapSettings()
|
||||||
|
val storage = WindowStateStorage(settings)
|
||||||
|
|
||||||
|
storage.save(width = 1200, height = 800, x = 100, y = 50)
|
||||||
|
|
||||||
|
val state = storage.load()
|
||||||
|
assertEquals(1200, state?.width)
|
||||||
|
assertEquals(800, state?.height)
|
||||||
|
assertEquals(100, state?.x)
|
||||||
|
assertEquals(50, state?.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loadReturnsNullWhenNothingSaved() {
|
||||||
|
val settings = MapSettings()
|
||||||
|
val storage = WindowStateStorage(settings)
|
||||||
|
|
||||||
|
assertNull(storage.load())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun saveOverwritesPreviousState() {
|
||||||
|
val settings = MapSettings()
|
||||||
|
val storage = WindowStateStorage(settings)
|
||||||
|
|
||||||
|
storage.save(width = 1200, height = 800, x = 100, y = 50)
|
||||||
|
storage.save(width = 1024, height = 768, x = 200, y = 75)
|
||||||
|
|
||||||
|
val state = storage.load()
|
||||||
|
assertEquals(1024, state?.width)
|
||||||
|
assertEquals(768, state?.height)
|
||||||
|
assertEquals(200, state?.x)
|
||||||
|
assertEquals(75, state?.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,12 +17,43 @@
|
|||||||
|
|
||||||
package ch.parano.myicek
|
package ch.parano.myicek
|
||||||
|
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.snapshotFlow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Window
|
import androidx.compose.ui.window.Window
|
||||||
|
import androidx.compose.ui.window.WindowPosition
|
||||||
import androidx.compose.ui.window.application
|
import androidx.compose.ui.window.application
|
||||||
|
import androidx.compose.ui.window.rememberWindowState
|
||||||
import ch.parano.myicek.auth.DesktopOAuthClient
|
import ch.parano.myicek.auth.DesktopOAuthClient
|
||||||
|
import com.russhwolf.settings.Settings
|
||||||
|
import java.util.prefs.Preferences
|
||||||
|
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
Window(onCloseRequest = ::exitApplication, title = "MyIce") {
|
val storage = WindowStateStorage(Settings())
|
||||||
|
val saved = storage.load()
|
||||||
|
val windowState = rememberWindowState(
|
||||||
|
width = saved?.width?.dp ?: 1024.dp,
|
||||||
|
height = saved?.height?.dp ?: 768.dp,
|
||||||
|
position = if (saved != null) WindowPosition(saved.x.dp, saved.y.dp) else WindowPosition(0.dp, 0.dp),
|
||||||
|
)
|
||||||
|
|
||||||
|
Window(
|
||||||
|
onCloseRequest = ::exitApplication,
|
||||||
|
title = "MyIce",
|
||||||
|
state = windowState,
|
||||||
|
) {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
snapshotFlow { windowState.size to windowState.position }
|
||||||
|
.collect { (size, position) ->
|
||||||
|
storage.save(
|
||||||
|
width = size.width.value.toInt(),
|
||||||
|
height = size.height.value.toInt(),
|
||||||
|
x = position.x.value.toInt(),
|
||||||
|
y = position.y.value.toInt(),
|
||||||
|
)
|
||||||
|
Preferences.userRoot().flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
App(oauthClient = DesktopOAuthClient())
|
App(oauthClient = DesktopOAuthClient())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user