Reformat to use more standard Android code style
Follows stock Android Studio styles for Kotlin and XML except that we use an indent of two and allow up to three consecutive empty lines.
This commit is contained in:
parent
abfc172642
commit
eebb3a589d
17 changed files with 512 additions and 481 deletions
|
|
@ -1,8 +1,7 @@
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.util.Properties
|
|
||||||
import org.gradle.kotlin.dsl.android
|
|
||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.util.Properties
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -47,7 +46,8 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
// Required even though we don't have any Java sources because it needs to match Kotlin's JVM version
|
// Required even though we don't have any Java sources because it needs to match Kotlin's JVM
|
||||||
|
// version.
|
||||||
targetCompatibility = JavaVersion.VERSION_25
|
targetCompatibility = JavaVersion.VERSION_25
|
||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
|
|
@ -62,7 +62,7 @@ kotlin {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Android runtime libraries
|
// Android runtime libraries.
|
||||||
implementation("com.google.android.material:material:1.13.0")
|
implementation("com.google.android.material:material:1.13.0")
|
||||||
implementation("androidx.activity:activity-compose:1.12.2")
|
implementation("androidx.activity:activity-compose:1.12.2")
|
||||||
implementation("androidx.core:core-ktx:1.17.0")
|
implementation("androidx.core:core-ktx:1.17.0")
|
||||||
|
|
@ -73,7 +73,7 @@ dependencies {
|
||||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.10.0")
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.10.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0")
|
||||||
// Other libraries
|
// Other libraries.
|
||||||
implementation("io.github.davidepianca98:kmqtt-common:1.0.0")
|
implementation("io.github.davidepianca98:kmqtt-common:1.0.0")
|
||||||
implementation("io.github.davidepianca98:kmqtt-client:1.0.0")
|
implementation("io.github.davidepianca98:kmqtt-client:1.0.0")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
|
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label = "Zanbur"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="Zanbur"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true">
|
android:supportsRtl="true">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ object AppState {
|
||||||
subscriber(group, newScene, source)
|
subscriber(group, newScene, source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun subscribeToCurrentScene(subscriber: CurrentSceneSubscriber) {
|
fun subscribeToCurrentScene(subscriber: CurrentSceneSubscriber) {
|
||||||
currentSceneSubscribers.add(subscriber)
|
currentSceneSubscribers.add(subscriber)
|
||||||
}
|
}
|
||||||
|
|
@ -25,6 +26,7 @@ object AppState {
|
||||||
subscriber(newGroup, source)
|
subscriber(newGroup, source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun subscribeToGroupAdded(subscriber: GroupAddedSubscriber) {
|
fun subscribeToGroupAdded(subscriber: GroupAddedSubscriber) {
|
||||||
groupAddedSubscribers.add(subscriber)
|
groupAddedSubscribers.add(subscriber)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,13 @@ object Config {
|
||||||
const val MQTT_TOPIC = "zigbee2mqtt"
|
const val MQTT_TOPIC = "zigbee2mqtt"
|
||||||
|
|
||||||
val groups = listOf(
|
val groups = listOf(
|
||||||
Group ( 0 , "All lights" , listOf (
|
Group(
|
||||||
|
0, "All lights", listOf(
|
||||||
Scene(0, "Warm"),
|
Scene(0, "Warm"),
|
||||||
Scene(1, "Warm dim"),
|
Scene(1, "Warm dim"),
|
||||||
Scene(2, "Warm dim with purple"),
|
Scene(2, "Warm dim with purple"),
|
||||||
Scene(100, "All off"),
|
Scene(100, "All off"),
|
||||||
) )
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
package com.kernelmaft.zanbur.network
|
package com.kernelmaft.zanbur.network
|
||||||
|
|
||||||
import com.kernelmaft.zanbur.common.Config.MQTT_SERVER_HOST
|
import com.kernelmaft.zanbur.common.Config
|
||||||
import com.kernelmaft.zanbur.common.Config.MQTT_SERVER_PORT
|
import io.github.davidepianca98.MQTTClient
|
||||||
import com.kernelmaft.zanbur.common.Config.MQTT_TOPIC
|
import io.github.davidepianca98.mqtt.MQTTVersion
|
||||||
import io.github.davidepianca98.*
|
import io.github.davidepianca98.mqtt.Subscription
|
||||||
import io.github.davidepianca98.mqtt.*
|
import io.github.davidepianca98.mqtt.packets.Qos
|
||||||
import io.github.davidepianca98.mqtt.MQTTVersion.*
|
import io.github.davidepianca98.mqtt.packets.mqtt.MQTTPublish
|
||||||
import io.github.davidepianca98.mqtt.packets.Qos.*
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import io.github.davidepianca98.mqtt.packets.mqtt.*
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -25,11 +24,12 @@ object MqttClient {
|
||||||
this.coroutineScope = coroutineScope
|
this.coroutineScope = coroutineScope
|
||||||
val json = Json { ignoreUnknownKeys = true }
|
val json = Json { ignoreUnknownKeys = true }
|
||||||
|
|
||||||
coroutineScope . launch ( IO + exceptionPrinter ) {
|
coroutineScope.launch(Dispatchers.IO + exceptionPrinter) {
|
||||||
client = MQTTClient ( MQTT5 , MQTT_SERVER_HOST , MQTT_SERVER_PORT , null ) {
|
client =
|
||||||
|
MQTTClient(MQTTVersion.MQTT5, Config.MQTT_SERVER_HOST, Config.MQTT_SERVER_PORT, null) {
|
||||||
for (handler in publishHandlers) handler(it, json)
|
for (handler in publishHandlers) handler(it, json)
|
||||||
}
|
}
|
||||||
client !! . subscribe ( listOf ( Subscription ( MQTT_TOPIC + "/#" ) ) )
|
client!!.subscribe(listOf(Subscription(Config.MQTT_TOPIC + "/#")))
|
||||||
|
|
||||||
client!!.run()
|
client!!.run()
|
||||||
}
|
}
|
||||||
|
|
@ -38,8 +38,8 @@ object MqttClient {
|
||||||
fun addPublishHandler(handler: MqttPublishHandler) = publishHandlers.add(handler)
|
fun addPublishHandler(handler: MqttPublishHandler) = publishHandlers.add(handler)
|
||||||
|
|
||||||
fun publish(topic: String, payload: UByteArray) {
|
fun publish(topic: String, payload: UByteArray) {
|
||||||
coroutineScope !! . launch ( IO + exceptionPrinter ) {
|
coroutineScope!!.launch(Dispatchers.IO + exceptionPrinter) {
|
||||||
client !! . publish ( false , AT_MOST_ONCE , topic , payload )
|
client!!.publish(false, Qos.AT_MOST_ONCE, topic, payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,23 @@
|
||||||
package com.kernelmaft.zanbur.network
|
package com.kernelmaft.zanbur.network
|
||||||
|
|
||||||
import com.kernelmaft.zanbur.common.*
|
import com.kernelmaft.zanbur.common.Config
|
||||||
import com.kernelmaft.zanbur.common.Config.MQTT_TOPIC
|
import com.kernelmaft.zanbur.common.Group
|
||||||
import kotlinx.serialization.*
|
import com.kernelmaft.zanbur.common.Scene
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun publishSceneChange(group: Group, newScene: Scene) {
|
fun publishSceneChange(group: Group, newScene: Scene) {
|
||||||
val topic = MQTT_TOPIC + "/" + group . name + "/set"
|
val topic = Config.MQTT_TOPIC + "/" + group.name + "/set"
|
||||||
val packet = Json.encodeToString(SceneRecallPacket(newScene.id))
|
val packet = Json.encodeToString(SceneRecallPacket(newScene.id))
|
||||||
.toByteArray()
|
.toByteArray()
|
||||||
.asUByteArray()
|
.asUByteArray()
|
||||||
MqttClient.publish(topic, packet)
|
MqttClient.publish(topic, packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable private data class SceneRecallPacket (
|
@Serializable
|
||||||
|
private data class SceneRecallPacket(
|
||||||
@SerialName("scene_recall") val sceneRecall: Int,
|
@SerialName("scene_recall") val sceneRecall: Int,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,44 @@
|
||||||
package com.kernelmaft.zanbur.ui
|
package com.kernelmaft.zanbur.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Arrangement.Center
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.ButtonDefaults.buttonColors
|
import androidx.compose.material3.ButtonDefaults.buttonColors
|
||||||
import androidx.compose.material3.ButtonDefaults.filledTonalButtonColors
|
import androidx.compose.material3.ButtonDefaults.filledTonalButtonColors
|
||||||
import androidx.compose.material3.ButtonDefaults.shape
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.ui.*
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
import androidx.compose.ui.Alignment
|
||||||
import com.kernelmaft.zanbur.common.*
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.kernelmaft.zanbur.common.Group
|
||||||
|
import com.kernelmaft.zanbur.common.Scene
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Composable fun AppFrame ( content : @Composable () -> Unit ) {
|
@Composable
|
||||||
|
fun AppFrame(content: @Composable () -> Unit) {
|
||||||
ZanburTheme {
|
ZanburTheme {
|
||||||
Scaffold { scaffoldPadding ->
|
Scaffold { scaffoldPadding ->
|
||||||
CenteringColumn ( Modifier . padding (scaffoldPadding) . fillMaxSize () ) {
|
CenteringColumn(
|
||||||
|
Modifier
|
||||||
|
.padding(scaffoldPadding)
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable fun SceneSwitcher ( group : Group , onSwitch : (Scene) -> Unit ) {
|
@Composable
|
||||||
|
fun SceneSwitcher(group: Group, onSwitch: (Scene) -> Unit) {
|
||||||
CenteringColumn {
|
CenteringColumn {
|
||||||
Text(group.name)
|
Text(group.name)
|
||||||
Spacer(Modifier.height(compactSpacing))
|
Spacer(Modifier.height(compactSpacing))
|
||||||
|
|
@ -33,14 +48,15 @@ import com.kernelmaft.zanbur.common.*
|
||||||
if (scene.id == group.currentScene?.id) buttonColors()
|
if (scene.id == group.currentScene?.id) buttonColors()
|
||||||
else filledTonalButtonColors()
|
else filledTonalButtonColors()
|
||||||
|
|
||||||
Button ( { onSwitch (scene) } , Modifier . fillMaxWidth () , true , shape , colors ) {
|
Button({ onSwitch(scene) }, Modifier.fillMaxWidth(), true, ButtonDefaults.shape, colors) {
|
||||||
Text(scene.name)
|
Text(scene.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable fun CenteringColumn (
|
@Composable
|
||||||
|
fun CenteringColumn(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
content: @Composable ColumnScope.() -> Unit,
|
content: @Composable ColumnScope.() -> Unit,
|
||||||
) = Column ( modifier , Center , CenterHorizontally , content )
|
) = Column(modifier, Arrangement.Center, Alignment.CenterHorizontally, content)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package com.kernelmaft.zanbur.ui
|
package com.kernelmaft.zanbur.ui
|
||||||
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.MutableState
|
||||||
import com.kernelmaft.zanbur.common.*
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import com.kernelmaft.zanbur.common.AppState
|
||||||
|
import com.kernelmaft.zanbur.common.Group
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -12,10 +14,12 @@ fun createGroupsComposeState () : MutableState < List <Group> > {
|
||||||
groups.value = groups.value.plus(newGroup)
|
groups.value = groups.value.plus(newGroup)
|
||||||
}
|
}
|
||||||
AppState.subscribeToCurrentScene { group, newScene, source ->
|
AppState.subscribeToCurrentScene { group, newScene, source ->
|
||||||
groups . value = groups . value . map { when ( it . id ) {
|
groups.value = groups.value.map {
|
||||||
|
when (it.id) {
|
||||||
group.id -> it.copy(currentScene = newScene)
|
group.id -> it.copy(currentScene = newScene)
|
||||||
else -> it
|
else -> it
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return groups
|
return groups
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
package com.kernelmaft.zanbur.ui
|
package com.kernelmaft.zanbur.ui
|
||||||
|
|
||||||
import android.os.*
|
import android.os.Bundle
|
||||||
import androidx.activity.compose.*
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.ui.*
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.ui.unit.*
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.*
|
import androidx.compose.ui.unit.dp
|
||||||
import com.kernelmaft.zanbur.common.*
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.kernelmaft.zanbur.common.ChangeSource.*
|
import com.kernelmaft.zanbur.common.AppState
|
||||||
import com.kernelmaft.zanbur.network.*
|
import com.kernelmaft.zanbur.common.ChangeSource
|
||||||
|
import com.kernelmaft.zanbur.common.Config
|
||||||
|
import com.kernelmaft.zanbur.network.MqttClient
|
||||||
|
import com.kernelmaft.zanbur.network.publishSceneChange
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -19,19 +22,19 @@ class MainActivity : EdgeToEdgeActivity () {
|
||||||
val groups = createGroupsComposeState()
|
val groups = createGroupsComposeState()
|
||||||
|
|
||||||
AppState.subscribeToCurrentScene { group, newScene, source ->
|
AppState.subscribeToCurrentScene { group, newScene, source ->
|
||||||
if ( source == Local ) {
|
if (source == ChangeSource.Local) {
|
||||||
publishSceneChange(group, newScene)
|
publishSceneChange(group, newScene)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Config . groups . forEach { AppState . addGroup ( it , Remote ) }
|
Config.groups.forEach { AppState.addGroup(it, ChangeSource.Remote) }
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
AppFrame {
|
AppFrame {
|
||||||
Column(Modifier.width(300.dp)) {
|
Column(Modifier.width(300.dp)) {
|
||||||
groups.value.forEach { group ->
|
groups.value.forEach { group ->
|
||||||
SceneSwitcher(group) { newScene ->
|
SceneSwitcher(group) { newScene ->
|
||||||
AppState . setCurrentScene ( group , newScene , Local )
|
AppState.setCurrentScene(group, newScene, ChangeSource.Local)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,21 @@
|
||||||
package com.kernelmaft.zanbur.ui
|
package com.kernelmaft.zanbur.ui
|
||||||
|
|
||||||
import android.os.*
|
import android.os.Bundle
|
||||||
import androidx.activity.*
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.MaterialTheme.shapes
|
import androidx.compose.material3.dynamicDarkColorScheme
|
||||||
import androidx.compose.material3.MaterialTheme.typography
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.platform.*
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.*
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val compactSpacing = 16.dp
|
val compactSpacing = 16.dp
|
||||||
|
|
||||||
@Composable fun ZanburTheme ( content : @Composable () -> Unit ) {
|
@Composable
|
||||||
|
fun ZanburTheme(content: @Composable () -> Unit) {
|
||||||
val colorScheme = run {
|
val colorScheme = run {
|
||||||
if (isSystemInDarkTheme())
|
if (isSystemInDarkTheme())
|
||||||
dynamicDarkColorScheme(LocalContext.current)
|
dynamicDarkColorScheme(LocalContext.current)
|
||||||
|
|
@ -22,7 +23,7 @@ val compactSpacing = 16 . dp
|
||||||
dynamicLightColorScheme(LocalContext.current)
|
dynamicLightColorScheme(LocalContext.current)
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialTheme ( colorScheme, shapes , typography , content )
|
MaterialTheme(colorScheme, MaterialTheme.shapes, MaterialTheme.typography, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
open class EdgeToEdgeActivity : ComponentActivity() {
|
open class EdgeToEdgeActivity : ComponentActivity() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue