Compare commits
No commits in common. "5410b4985fb1e024fd8e44d063a8875761adb5f9" and "4ca76ac59364b241899158045b30b2ef59e4a3f0" have entirely different histories.
5410b4985f
...
4ca76ac593
16 changed files with 156 additions and 136 deletions
|
@ -9,20 +9,20 @@ val keystoreProperties = Properties ()
|
||||||
keystoreProperties . load ( FileInputStream ( rootProject . file ("keystore.properties") ) )
|
keystoreProperties . load ( FileInputStream ( rootProject . file ("keystore.properties") ) )
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id ("com.android.application") . version ("8.8.0")
|
id ("com.android.application") . version ("8.7.2")
|
||||||
id ("org.jetbrains.kotlin.android") . version ("2.1.10")
|
id ("org.jetbrains.kotlin.android") . version ("2.0.0")
|
||||||
id ("org.jetbrains.kotlin.plugin.compose") . version ("2.1.10")
|
id ("org.jetbrains.kotlin.plugin.compose") . version ("2.0.0")
|
||||||
id ("org.jetbrains.kotlin.plugin.serialization") . version ("2.1.10")
|
id ("org.jetbrains.kotlin.plugin.serialization") . version ("2.0.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.kernelmaft.zanbur"
|
namespace = "com.kernelmaft.zanbur"
|
||||||
compileSdk = 35
|
compileSdk = 34
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.kernelmaft.zanbur"
|
applicationId = "com.kernelmaft.zanbur"
|
||||||
minSdk = 35
|
minSdk = 31
|
||||||
targetSdk = 35
|
targetSdk = 34
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
}
|
}
|
||||||
|
@ -47,11 +47,10 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
// Required even though we don't have any Java sources because it needs to match Kotlin's JVM version
|
targetCompatibility = JavaVersion . VERSION_11
|
||||||
targetCompatibility = JavaVersion . VERSION_23
|
|
||||||
}
|
}
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "23"
|
jvmTarget = "11"
|
||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
|
@ -61,18 +60,19 @@ android {
|
||||||
dependencies {
|
dependencies {
|
||||||
// Android runtime libraries
|
// Android runtime libraries
|
||||||
implementation ( "com.google.android.material" , "material" , "1.12.0" )
|
implementation ( "com.google.android.material" , "material" , "1.12.0" )
|
||||||
implementation ( "androidx.activity" , "activity-compose" , "1.10.0" )
|
implementation ( "androidx.activity" , "activity-compose" , "1.9.3" )
|
||||||
implementation ( "androidx.core" , "core-ktx" , "1.15.0" )
|
implementation ( "androidx.core" , "core-ktx" , "1.13.1" )
|
||||||
implementation ( "androidx.compose.material3" , "material3" , "1.3.1" )
|
implementation ( "androidx.compose.material3" , "material3" , "1.3.1" )
|
||||||
implementation ( "androidx.compose.ui" , "ui" , "1.7.7" )
|
implementation ( "androidx.compose.ui" , "ui" , "1.7.5" )
|
||||||
implementation ( "androidx.compose.ui" , "ui-graphics" , "1.7.7" )
|
implementation ( "androidx.compose.ui" , "ui-graphics" , "1.7.5" )
|
||||||
debugImplementation ( "androidx.compose.ui" , "ui-tooling" , "1.7.7" )
|
debugImplementation ( "androidx.compose.ui" , "ui-tooling" , "1.7.5" )
|
||||||
|
implementation ( "androidx.datastore" , "datastore-preferences" , "1.1.1" )
|
||||||
implementation ( "androidx.lifecycle" , "lifecycle-runtime-ktx" , "2.8.7" )
|
implementation ( "androidx.lifecycle" , "lifecycle-runtime-ktx" , "2.8.7" )
|
||||||
implementation ( "org.jetbrains.kotlinx" , "kotlinx-coroutines-android" , "1.10.1" )
|
implementation ( "org.jetbrains.kotlinx" , "kotlinx-coroutines-android" , "1.9.0" )
|
||||||
implementation ( "org.jetbrains.kotlinx" , "kotlinx-serialization-json" , "1.8.0" )
|
implementation ( "org.jetbrains.kotlinx" , "kotlinx-serialization-json" , "1.7.3" )
|
||||||
// Other libraries
|
// Other libraries
|
||||||
implementation ( "io.github.davidepianca98" , "kmqtt-common" , "1.0.0" )
|
implementation ( "io.github.davidepianca98" , "kmqtt-common" , "0.4.8" )
|
||||||
implementation ( "io.github.davidepianca98" , "kmqtt-client" , "1.0.0" )
|
implementation ( "io.github.davidepianca98" , "kmqtt-client" , "0.4.8" )
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks . withType ( KotlinCompile :: class ) . all {
|
tasks . withType ( KotlinCompile :: class ) . all {
|
||||||
|
|
|
@ -4,14 +4,17 @@
|
||||||
<uses-permission android:name = "android.permission.INTERNET" />
|
<uses-permission android:name = "android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label = "Zanbur"
|
|
||||||
android:icon = "@mipmap/ic_launcher"
|
android:icon = "@mipmap/ic_launcher"
|
||||||
|
android:label = "@string/app_name"
|
||||||
android:roundIcon = "@mipmap/ic_launcher_round"
|
android:roundIcon = "@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl = "true" >
|
android:supportsRtl = "true"
|
||||||
|
android:theme = "@style/Theme.Zanbur" >
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name = "com.kernelmaft.zanbur.ui.MainActivity"
|
android:name = ".MainActivity"
|
||||||
android:exported = "true" >
|
android:exported = "true"
|
||||||
|
android:label = "@string/title_activity_main"
|
||||||
|
android:theme = "@style/Theme.Zanbur" >
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name = "android.intent.action.MAIN" />
|
<action android:name = "android.intent.action.MAIN" />
|
||||||
|
|
25
app/src/main/java/com/kernelmaft/zanbur/app-state.kt
Normal file
25
app/src/main/java/com/kernelmaft/zanbur/app-state.kt
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
object AppState {
|
||||||
|
val groups : List <Group> get () = groupsAsMutable
|
||||||
|
private var groupsAsMutable : List <Group> = emptyList ()
|
||||||
|
|
||||||
|
private val subscribers : MutableList < ( List <Group> ) -> Unit > = mutableListOf ()
|
||||||
|
|
||||||
|
fun subscribe ( subscriber : ( List <Group> ) -> 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) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
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 <CurrentSceneSubscriber> = mutableListOf ()
|
|
||||||
private val groupAddedSubscribers : MutableList <GroupAddedSubscriber> = 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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.kernelmaft.zanbur.ui
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.Arrangement.Center
|
import androidx.compose.foundation.layout.Arrangement.Center
|
||||||
|
@ -9,7 +9,6 @@ import androidx.compose.material3.ButtonDefaults.shape
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.*
|
import androidx.compose.ui.*
|
||||||
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||||
import com.kernelmaft.zanbur.common.*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.kernelmaft.zanbur.common
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
|
|
||||||
|
|
65
app/src/main/java/com/kernelmaft/zanbur/main.kt
Normal file
65
app/src/main/java/com/kernelmaft/zanbur/main.kt
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
package com.kernelmaft.zanbur.network
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
import com.kernelmaft.zanbur.common.Config.MQTT_SERVER_HOST
|
import MQTTClient
|
||||||
import com.kernelmaft.zanbur.common.Config.MQTT_SERVER_PORT
|
import com.kernelmaft.zanbur.Config.MQTT_SERVER_HOST
|
||||||
import com.kernelmaft.zanbur.common.Config.MQTT_TOPIC
|
import com.kernelmaft.zanbur.Config.MQTT_SERVER_PORT
|
||||||
import io.github.davidepianca98.*
|
import com.kernelmaft.zanbur.Config.MQTT_TOPIC
|
||||||
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.*
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
|
import mqtt.*
|
||||||
|
import mqtt.MQTTVersion.*
|
||||||
|
import mqtt.packets.Qos.*
|
||||||
|
import mqtt.packets.mqtt.*
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.kernelmaft.zanbur.common
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
|
|
||||||
|
|
10
app/src/main/java/com/kernelmaft/zanbur/persistence.kt
Normal file
10
app/src/main/java/com/kernelmaft/zanbur/persistence.kt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
|
import android.content.*
|
||||||
|
import androidx.datastore.core.*
|
||||||
|
import androidx.datastore.preferences.*
|
||||||
|
import androidx.datastore.preferences.core.*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
val Context . dataStore : DataStore <Preferences> by preferencesDataStore ("state")
|
|
@ -1,4 +1,4 @@
|
||||||
package com.kernelmaft.zanbur.ui
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
import android.os.*
|
import android.os.*
|
||||||
import androidx.activity.*
|
import androidx.activity.*
|
||||||
|
@ -29,6 +29,7 @@ open class EdgeToEdgeActivity : ComponentActivity () {
|
||||||
override fun onCreate ( savedInstanceState : Bundle ? ) {
|
override fun onCreate ( savedInstanceState : Bundle ? ) {
|
||||||
super . onCreate (savedInstanceState)
|
super . onCreate (savedInstanceState)
|
||||||
|
|
||||||
|
enableEdgeToEdge ()
|
||||||
actionBar ?. hide ()
|
actionBar ?. hide ()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
package com.kernelmaft.zanbur.ui
|
|
||||||
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import com.kernelmaft.zanbur.common.*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun createGroupsComposeState () : MutableState < List <Group> > {
|
|
||||||
val groups : MutableState < List <Group> > = 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
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.kernelmaft.zanbur.network
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
import com.kernelmaft.zanbur.common.*
|
import com.kernelmaft.zanbur.Config.MQTT_TOPIC
|
||||||
import com.kernelmaft.zanbur.common.Config.MQTT_TOPIC
|
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
|
|
6
app/src/main/res/values/strings.xml
Normal file
6
app/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name = "app_name" >Zanbur</string>
|
||||||
|
<string name = "title_activity_main" >Zanbur</string>
|
||||||
|
|
||||||
|
</resources>
|
8
app/src/main/res/values/themes.xml
Normal file
8
app/src/main/res/values/themes.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style
|
||||||
|
name = "Theme.Zanbur"
|
||||||
|
parent = "Theme.Material3.DayNight" >
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
Loading…
Add table
Add a link
Reference in a new issue