diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f29eafc..63a6902 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,20 +9,20 @@ val keystoreProperties = Properties () keystoreProperties . load ( FileInputStream ( rootProject . file ("keystore.properties") ) ) plugins { - id ("com.android.application") . version ("8.7.2") - id ("org.jetbrains.kotlin.android") . version ("2.0.0") - id ("org.jetbrains.kotlin.plugin.compose") . version ("2.0.0") - id ("org.jetbrains.kotlin.plugin.serialization") . version ("2.0.0") + id ("com.android.application") . version ("8.8.0") + id ("org.jetbrains.kotlin.android") . version ("2.1.10") + id ("org.jetbrains.kotlin.plugin.compose") . version ("2.1.10") + id ("org.jetbrains.kotlin.plugin.serialization") . version ("2.1.10") } android { namespace = "com.kernelmaft.zanbur" - compileSdk = 34 + compileSdk = 35 defaultConfig { applicationId = "com.kernelmaft.zanbur" - minSdk = 31 - targetSdk = 34 + minSdk = 35 + targetSdk = 35 versionCode = 1 versionName = "1.0" } @@ -47,10 +47,11 @@ android { } } compileOptions { - targetCompatibility = JavaVersion . VERSION_11 + // Required even though we don't have any Java sources because it needs to match Kotlin's JVM version + targetCompatibility = JavaVersion . VERSION_23 } kotlinOptions { - jvmTarget = "11" + jvmTarget = "23" } buildFeatures { compose = true @@ -60,19 +61,18 @@ android { dependencies { // Android runtime libraries implementation ( "com.google.android.material" , "material" , "1.12.0" ) - implementation ( "androidx.activity" , "activity-compose" , "1.9.3" ) - implementation ( "androidx.core" , "core-ktx" , "1.13.1" ) + implementation ( "androidx.activity" , "activity-compose" , "1.10.0" ) + implementation ( "androidx.core" , "core-ktx" , "1.15.0" ) implementation ( "androidx.compose.material3" , "material3" , "1.3.1" ) - implementation ( "androidx.compose.ui" , "ui" , "1.7.5" ) - implementation ( "androidx.compose.ui" , "ui-graphics" , "1.7.5" ) - debugImplementation ( "androidx.compose.ui" , "ui-tooling" , "1.7.5" ) - implementation ( "androidx.datastore" , "datastore-preferences" , "1.1.1" ) + implementation ( "androidx.compose.ui" , "ui" , "1.7.7" ) + implementation ( "androidx.compose.ui" , "ui-graphics" , "1.7.7" ) + debugImplementation ( "androidx.compose.ui" , "ui-tooling" , "1.7.7" ) implementation ( "androidx.lifecycle" , "lifecycle-runtime-ktx" , "2.8.7" ) - implementation ( "org.jetbrains.kotlinx" , "kotlinx-coroutines-android" , "1.9.0" ) - implementation ( "org.jetbrains.kotlinx" , "kotlinx-serialization-json" , "1.7.3" ) + implementation ( "org.jetbrains.kotlinx" , "kotlinx-coroutines-android" , "1.10.1" ) + implementation ( "org.jetbrains.kotlinx" , "kotlinx-serialization-json" , "1.8.0" ) // Other libraries - implementation ( "io.github.davidepianca98" , "kmqtt-common" , "0.4.8" ) - implementation ( "io.github.davidepianca98" , "kmqtt-client" , "0.4.8" ) + implementation ( "io.github.davidepianca98" , "kmqtt-common" , "1.0.0" ) + implementation ( "io.github.davidepianca98" , "kmqtt-client" , "1.0.0" ) } tasks . withType ( KotlinCompile :: class ) . all { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1acccf6..90a24a3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,17 +4,14 @@ + android:supportsRtl = "true" > + android:name = "com.kernelmaft.zanbur.ui.MainActivity" + android:exported = "true" > diff --git a/app/src/main/java/com/kernelmaft/zanbur/app-state.kt b/app/src/main/java/com/kernelmaft/zanbur/app-state.kt deleted file mode 100644 index 52c4991..0000000 --- a/app/src/main/java/com/kernelmaft/zanbur/app-state.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.kernelmaft.zanbur - - - -object AppState { - val groups : List get () = groupsAsMutable - private var groupsAsMutable : List = emptyList () - - private val subscribers : MutableList < ( List ) -> Unit > = mutableListOf () - - fun subscribe ( subscriber : ( List ) -> Unit ) = subscribers . add (subscriber) - - fun addGroup ( group : Group ) { - groupsAsMutable += group - subscribers . forEach { it (groups) } - } - - fun setCurrentScene ( groupId : Int , scene : Scene ) { - groupsAsMutable = groupsAsMutable . mapIndexed { index , group -> - if ( index == groupId ) group . copy ( currentScene = scene ) - else group - } - subscribers . forEach { it (groups) } - } -} diff --git a/app/src/main/java/com/kernelmaft/zanbur/common/app-state.kt b/app/src/main/java/com/kernelmaft/zanbur/common/app-state.kt new file mode 100644 index 0000000..6abdb42 --- /dev/null +++ b/app/src/main/java/com/kernelmaft/zanbur/common/app-state.kt @@ -0,0 +1,31 @@ +package com.kernelmaft.zanbur.common + + + +enum class ChangeSource { Local , Remote } + +typealias CurrentSceneSubscriber = ( Group , Scene , ChangeSource ) -> Unit +typealias GroupAddedSubscriber = ( Group , ChangeSource ) -> Unit + +object AppState { + private val currentSceneSubscribers : MutableList = mutableListOf () + private val groupAddedSubscribers : MutableList = mutableListOf () + + fun setCurrentScene ( group : Group , newScene : Scene , source : ChangeSource ) { + for ( subscriber in currentSceneSubscribers ) { + subscriber ( group , newScene , source ) + } + } + fun subscribeToCurrentScene ( subscriber : CurrentSceneSubscriber ) { + currentSceneSubscribers . add (subscriber) + } + + fun addGroup ( newGroup : Group , source : ChangeSource ) { + for ( subscriber in groupAddedSubscribers ) { + subscriber ( newGroup , source ) + } + } + fun subscribeToGroupAdded ( subscriber : GroupAddedSubscriber ) { + groupAddedSubscribers . add (subscriber) + } +} diff --git a/app/src/main/java/com/kernelmaft/zanbur/config.kt b/app/src/main/java/com/kernelmaft/zanbur/common/config.kt similarity index 92% rename from app/src/main/java/com/kernelmaft/zanbur/config.kt rename to app/src/main/java/com/kernelmaft/zanbur/common/config.kt index ef1f384..3e4ddda 100644 --- a/app/src/main/java/com/kernelmaft/zanbur/config.kt +++ b/app/src/main/java/com/kernelmaft/zanbur/common/config.kt @@ -1,4 +1,4 @@ -package com.kernelmaft.zanbur +package com.kernelmaft.zanbur.common diff --git a/app/src/main/java/com/kernelmaft/zanbur/ontology.kt b/app/src/main/java/com/kernelmaft/zanbur/common/ontology.kt similarity index 83% rename from app/src/main/java/com/kernelmaft/zanbur/ontology.kt rename to app/src/main/java/com/kernelmaft/zanbur/common/ontology.kt index 4bec33e..86e8a1c 100644 --- a/app/src/main/java/com/kernelmaft/zanbur/ontology.kt +++ b/app/src/main/java/com/kernelmaft/zanbur/common/ontology.kt @@ -1,4 +1,4 @@ -package com.kernelmaft.zanbur +package com.kernelmaft.zanbur.common diff --git a/app/src/main/java/com/kernelmaft/zanbur/main.kt b/app/src/main/java/com/kernelmaft/zanbur/main.kt deleted file mode 100644 index 769890f..0000000 --- a/app/src/main/java/com/kernelmaft/zanbur/main.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.kernelmaft.zanbur - -import android.os.* -import androidx.activity.compose.* -import androidx.compose.foundation.layout.* -import androidx.compose.runtime.* -import androidx.compose.ui.* -import androidx.compose.ui.unit.* -import androidx.datastore.preferences.core.* -import androidx.lifecycle.* -import kotlinx.coroutines.* -import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.flow.* - - - -class MainActivity : EdgeToEdgeActivity () { - override fun onCreate ( savedInstanceState : Bundle ? ) { - super . onCreate (savedInstanceState) - - Config . groups . forEach { AppState . addGroup (it) } - - var groups by mutableStateOf ( AppState . groups ) - AppState . subscribe { groups = it } - - lifecycleScope . launch (IO) { - val prefs = dataStore . data . firstOrNull () - if ( prefs != null ) { - val savedSceneName = prefs [ stringPreferencesKey ("scene") ] - if ( savedSceneName != null ) { - val savedScene = AppState . groups [0] . scenes . find { it . name == savedSceneName } - if ( savedScene != null ) { - AppState . setCurrentScene ( 0 , savedScene ) - } - } - } - } - - setContent { - AppFrame { - Column ( Modifier . width ( 300 . dp ) ) { - groups . forEach { group -> - SceneSwitcher (group) { - AppState . setCurrentScene ( group . id , it ) - publishSceneChange ( group , it ) - } - } - } - } - } - - MqttClient . run (lifecycleScope) - } - - override fun onStop () { - super . onStop () - - val currentScene = AppState . groups [0] . currentScene - if ( currentScene != null ) lifecycleScope . launch (IO) { - applicationContext . dataStore . edit { - it [ stringPreferencesKey ("scene") ] = currentScene . name - } - } - } -} diff --git a/app/src/main/java/com/kernelmaft/zanbur/mqtt.kt b/app/src/main/java/com/kernelmaft/zanbur/network/mqtt.kt similarity index 70% rename from app/src/main/java/com/kernelmaft/zanbur/mqtt.kt rename to app/src/main/java/com/kernelmaft/zanbur/network/mqtt.kt index 5d633ac..212d73a 100644 --- a/app/src/main/java/com/kernelmaft/zanbur/mqtt.kt +++ b/app/src/main/java/com/kernelmaft/zanbur/network/mqtt.kt @@ -1,16 +1,16 @@ -package com.kernelmaft.zanbur +package com.kernelmaft.zanbur.network -import MQTTClient -import com.kernelmaft.zanbur.Config.MQTT_SERVER_HOST -import com.kernelmaft.zanbur.Config.MQTT_SERVER_PORT -import com.kernelmaft.zanbur.Config.MQTT_TOPIC +import com.kernelmaft.zanbur.common.Config.MQTT_SERVER_HOST +import com.kernelmaft.zanbur.common.Config.MQTT_SERVER_PORT +import com.kernelmaft.zanbur.common.Config.MQTT_TOPIC +import io.github.davidepianca98.* +import io.github.davidepianca98.mqtt.* +import io.github.davidepianca98.mqtt.MQTTVersion.* +import io.github.davidepianca98.mqtt.packets.Qos.* +import io.github.davidepianca98.mqtt.packets.mqtt.* import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.serialization.json.* -import mqtt.* -import mqtt.MQTTVersion.* -import mqtt.packets.Qos.* -import mqtt.packets.mqtt.* diff --git a/app/src/main/java/com/kernelmaft/zanbur/zigbee2mqtt.kt b/app/src/main/java/com/kernelmaft/zanbur/network/zigbee2mqtt.kt similarity index 77% rename from app/src/main/java/com/kernelmaft/zanbur/zigbee2mqtt.kt rename to app/src/main/java/com/kernelmaft/zanbur/network/zigbee2mqtt.kt index 0dc2d88..e59269f 100644 --- a/app/src/main/java/com/kernelmaft/zanbur/zigbee2mqtt.kt +++ b/app/src/main/java/com/kernelmaft/zanbur/network/zigbee2mqtt.kt @@ -1,6 +1,7 @@ -package com.kernelmaft.zanbur +package com.kernelmaft.zanbur.network -import com.kernelmaft.zanbur.Config.MQTT_TOPIC +import com.kernelmaft.zanbur.common.* +import com.kernelmaft.zanbur.common.Config.MQTT_TOPIC import kotlinx.serialization.* import kotlinx.serialization.json.* diff --git a/app/src/main/java/com/kernelmaft/zanbur/persistence.kt b/app/src/main/java/com/kernelmaft/zanbur/persistence.kt deleted file mode 100644 index 9110f71..0000000 --- a/app/src/main/java/com/kernelmaft/zanbur/persistence.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.kernelmaft.zanbur - -import android.content.* -import androidx.datastore.core.* -import androidx.datastore.preferences.* -import androidx.datastore.preferences.core.* - - - -val Context . dataStore : DataStore by preferencesDataStore ("state") diff --git a/app/src/main/java/com/kernelmaft/zanbur/components.kt b/app/src/main/java/com/kernelmaft/zanbur/ui/components.kt similarity index 94% rename from app/src/main/java/com/kernelmaft/zanbur/components.kt rename to app/src/main/java/com/kernelmaft/zanbur/ui/components.kt index e2d54e9..03fc6ac 100644 --- a/app/src/main/java/com/kernelmaft/zanbur/components.kt +++ b/app/src/main/java/com/kernelmaft/zanbur/ui/components.kt @@ -1,4 +1,4 @@ -package com.kernelmaft.zanbur +package com.kernelmaft.zanbur.ui import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Arrangement.Center @@ -9,6 +9,7 @@ import androidx.compose.material3.ButtonDefaults.shape import androidx.compose.runtime.* import androidx.compose.ui.* import androidx.compose.ui.Alignment.Companion.CenterHorizontally +import com.kernelmaft.zanbur.common.* diff --git a/app/src/main/java/com/kernelmaft/zanbur/ui/compose-state.kt b/app/src/main/java/com/kernelmaft/zanbur/ui/compose-state.kt new file mode 100644 index 0000000..212f254 --- /dev/null +++ b/app/src/main/java/com/kernelmaft/zanbur/ui/compose-state.kt @@ -0,0 +1,22 @@ +package com.kernelmaft.zanbur.ui + +import androidx.compose.runtime.* +import com.kernelmaft.zanbur.common.* + + + +fun createGroupsComposeState () : MutableState < List > { + val groups : MutableState < List > = mutableStateOf ( emptyList () ) + + AppState . subscribeToGroupAdded { newGroup , _ -> + groups . value = groups . value . plus (newGroup) + } + AppState . subscribeToCurrentScene { group , newScene , source -> + groups . value = groups . value . map { when ( it . id ) { + group . id -> it . copy ( currentScene = newScene ) + else -> it + } } + } + + return groups +} diff --git a/app/src/main/java/com/kernelmaft/zanbur/ui/main-activity.kt b/app/src/main/java/com/kernelmaft/zanbur/ui/main-activity.kt new file mode 100644 index 0000000..147fd0c --- /dev/null +++ b/app/src/main/java/com/kernelmaft/zanbur/ui/main-activity.kt @@ -0,0 +1,43 @@ +package com.kernelmaft.zanbur.ui + +import android.os.* +import androidx.activity.compose.* +import androidx.compose.foundation.layout.* +import androidx.compose.ui.* +import androidx.compose.ui.unit.* +import androidx.lifecycle.* +import com.kernelmaft.zanbur.common.* +import com.kernelmaft.zanbur.common.ChangeSource.* +import com.kernelmaft.zanbur.network.* + + + +class MainActivity : EdgeToEdgeActivity () { + override fun onCreate ( savedInstanceState : Bundle ? ) { + super . onCreate (savedInstanceState) + + val groups = createGroupsComposeState () + + AppState . subscribeToCurrentScene { group , newScene , source -> + if ( source == Local ) { + publishSceneChange ( group , newScene ) + } + } + + Config . groups . forEach { AppState . addGroup ( it , Remote ) } + + setContent { + AppFrame { + Column ( Modifier . width ( 300 . dp ) ) { + groups . value . forEach { group -> + SceneSwitcher (group) { newScene -> + AppState . setCurrentScene ( group , newScene , Local ) + } + } + } + } + } + + MqttClient . run (lifecycleScope) + } +} diff --git a/app/src/main/java/com/kernelmaft/zanbur/theme.kt b/app/src/main/java/com/kernelmaft/zanbur/ui/theme.kt similarity index 94% rename from app/src/main/java/com/kernelmaft/zanbur/theme.kt rename to app/src/main/java/com/kernelmaft/zanbur/ui/theme.kt index 8a95cbc..0da4c53 100644 --- a/app/src/main/java/com/kernelmaft/zanbur/theme.kt +++ b/app/src/main/java/com/kernelmaft/zanbur/ui/theme.kt @@ -1,4 +1,4 @@ -package com.kernelmaft.zanbur +package com.kernelmaft.zanbur.ui import android.os.* import androidx.activity.* @@ -29,7 +29,6 @@ open class EdgeToEdgeActivity : ComponentActivity () { override fun onCreate ( savedInstanceState : Bundle ? ) { super . onCreate (savedInstanceState) - enableEdgeToEdge () actionBar ?. hide () } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml deleted file mode 100644 index 30feaa0..0000000 --- a/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - Zanbur - Zanbur - - diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml deleted file mode 100644 index ced7955..0000000 --- a/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - -