Project setup
This commit is contained in:
parent
b131fceb1f
commit
d129c407d3
19 changed files with 713 additions and 0 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,8 +1,11 @@
|
||||||
/app/build/
|
/app/build/
|
||||||
|
/app/release/
|
||||||
/.idea/
|
/.idea/
|
||||||
/gradle/wrapper/
|
/gradle/wrapper/
|
||||||
/gradlew
|
/gradlew
|
||||||
/gradlew.bat
|
/gradlew.bat
|
||||||
|
/keystore.properties
|
||||||
|
/.kotlin/
|
||||||
|
|
||||||
# Android Studio defaults
|
# Android Studio defaults
|
||||||
*.iml
|
*.iml
|
||||||
|
|
62
app/build.gradle.kts
Normal file
62
app/build.gradle.kts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
plugins {
|
||||||
|
alias ( libs . plugins . android . application )
|
||||||
|
alias ( libs . plugins . kotlin . android )
|
||||||
|
alias ( libs . plugins . kotlin . compose )
|
||||||
|
alias ( libs . plugins . kotlin . serialization )
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.kernelmaft.zanbur"
|
||||||
|
compileSdk = 34
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "com.kernelmaft.zanbur"
|
||||||
|
minSdk = 31
|
||||||
|
targetSdk = 34
|
||||||
|
versionCode = 1
|
||||||
|
versionName = "1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
targetCompatibility = JavaVersion . VERSION_11
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "11"
|
||||||
|
}
|
||||||
|
buildToolsVersion = "34.0.0"
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
composeOptions {
|
||||||
|
kotlinCompilerExtensionVersion = "1.5.14"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Runtime libraries
|
||||||
|
implementation ( libs . android . material )
|
||||||
|
implementation ( libs . androidx . activity . compose )
|
||||||
|
implementation ( libs . androidx . core . ktx )
|
||||||
|
implementation ( libs . androidx . compose . material3 )
|
||||||
|
implementation ( libs . androidx . compose . ui )
|
||||||
|
implementation ( libs . androidx . compose . ui . graphics )
|
||||||
|
debugImplementation ( libs . androidx . compose . ui . tooling )
|
||||||
|
implementation ( libs . androidx . lifecycle . runtime . ktx )
|
||||||
|
implementation ( libs . kotlinx . coroutines . android )
|
||||||
|
implementation ( libs . kotlinx . serialization . json )
|
||||||
|
// Other libraries
|
||||||
|
implementation ( libs . kmqtt . common )
|
||||||
|
implementation ( libs . kmqtt . client )
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks . withType ( org . jetbrains . kotlin . gradle . tasks . KotlinCompile :: class ) . all {
|
||||||
|
compilerOptions {
|
||||||
|
freeCompilerArgs . addAll ( "-opt-in=kotlin.ExperimentalUnsignedTypes" )
|
||||||
|
}
|
||||||
|
}
|
28
app/src/main/AndroidManifest.xml
Normal file
28
app/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.Zanbur">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:label="@string/title_activity_main"
|
||||||
|
android:theme="@style/Theme.Zanbur">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
17
app/src/main/java/com/kernelmaft/zanbur/app-state.kt
Normal file
17
app/src/main/java/com/kernelmaft/zanbur/app-state.kt
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
|
||||||
|
|
||||||
|
typealias MutableStateMap < Key , Value > = MutableState < Map < Key , Value > >
|
||||||
|
|
||||||
|
object AppState {
|
||||||
|
val devices : MutableStateMap < DeviceName , Device > = mutableStateOf ( emptyMap () )
|
||||||
|
val deviceStates : MutableStateMap < Device , LampState > = mutableStateOf ( emptyMap () )
|
||||||
|
val scenes : MutableStateMap < SceneName , Scene > = mutableStateOf ( emptyMap () )
|
||||||
|
val currentScene : MutableState < Scene ? > = mutableStateOf (null)
|
||||||
|
|
||||||
|
fun putDevice ( device : Device ) { devices . value += Pair ( device . name , device ) }
|
||||||
|
fun putScene ( scene : Scene ) { scenes . value += Pair ( scene . name , scene ) }
|
||||||
|
}
|
30
app/src/main/java/com/kernelmaft/zanbur/config.kt
Normal file
30
app/src/main/java/com/kernelmaft/zanbur/config.kt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
|
|
||||||
|
object Config {
|
||||||
|
const val MQTT_SERVER_HOST = "antoinette.kernelmaft.com"
|
||||||
|
const val MQTT_SERVER_PORT = 1883
|
||||||
|
const val MQTT_TOPIC = "zigbee2mqtt"
|
||||||
|
|
||||||
|
private val deskLamp = TemperatureLamp ( DeviceName ("Desk lamp") )
|
||||||
|
private val standingLamp = TemperatureLamp ( DeviceName ("Standing lamp") )
|
||||||
|
private val diningTableLamp = TemperatureLamp ( DeviceName ("Dining table lamp") )
|
||||||
|
private val ledStrip = RgbLamp ( DeviceName ("LED strip") )
|
||||||
|
|
||||||
|
val devices = listOf ( deskLamp , standingLamp , diningTableLamp , ledStrip )
|
||||||
|
|
||||||
|
val scenes = listOf (
|
||||||
|
Scene ( SceneName ("All off") , mapOf (
|
||||||
|
Pair ( deskLamp , TemperatureLampState ( Power . OFF , 254 , 250 ) ) ,
|
||||||
|
Pair ( standingLamp , TemperatureLampState ( Power . OFF , 254 , 250 ) ) ,
|
||||||
|
Pair ( diningTableLamp , TemperatureLampState ( Power . OFF , 254 , 250 ) ) ,
|
||||||
|
Pair ( ledStrip , RgbLampState ( Power . OFF , 254 , Cie1931Color ( 0.0 , 0.0 ) ) ) ,
|
||||||
|
) ) ,
|
||||||
|
Scene ( SceneName ("Evening") , mapOf (
|
||||||
|
Pair ( deskLamp , TemperatureLampState ( Power . ON , 128 , 454 ) ) ,
|
||||||
|
Pair ( standingLamp , TemperatureLampState ( Power . ON , 192 , 454 ) ) ,
|
||||||
|
Pair ( diningTableLamp , TemperatureLampState ( Power . ON , 192 , 454 ) ) ,
|
||||||
|
Pair ( ledStrip , RgbLampState ( Power . ON , 128 , Cie1931Color ( 0.25 , 0.05 ) ) ) ,
|
||||||
|
) ) ,
|
||||||
|
)
|
||||||
|
}
|
112
app/src/main/java/com/kernelmaft/zanbur/main.kt
Normal file
112
app/src/main/java/com/kernelmaft/zanbur/main.kt
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.compose.foundation.layout.Arrangement.Center
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
|
||||||
|
|
||||||
|
class MainActivity : ComponentActivity () {
|
||||||
|
override fun onCreate ( savedInstanceState : Bundle ? ) {
|
||||||
|
super . onCreate (savedInstanceState)
|
||||||
|
|
||||||
|
val devices by AppState . devices
|
||||||
|
val scenes by AppState . scenes
|
||||||
|
var currentScene by AppState . currentScene
|
||||||
|
|
||||||
|
for ( device in Config . devices ) AppState . putDevice (device)
|
||||||
|
for ( scene in Config . scenes ) AppState . putScene (scene)
|
||||||
|
|
||||||
|
enableEdgeToEdge ()
|
||||||
|
setContent {
|
||||||
|
ZanburTheme {
|
||||||
|
Scaffold { scaffoldPadding ->
|
||||||
|
val wrapperModifier = Modifier
|
||||||
|
.padding(scaffoldPadding)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight()
|
||||||
|
|
||||||
|
Column ( wrapperModifier , Center , CenterHorizontally ) {
|
||||||
|
SceneSwitcher ( scenes . values . map { it . name } , currentScene ?. name ) {
|
||||||
|
val newScene = scenes . getValue (it)
|
||||||
|
currentScene = newScene
|
||||||
|
handleSceneChange (newScene)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttClient . run (lifecycleScope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable private fun SceneSwitcher (
|
||||||
|
scenes : Collection <SceneName> ,
|
||||||
|
currentScene : SceneName ? ,
|
||||||
|
onSwitch : (SceneName) -> Unit ,
|
||||||
|
) = Column {
|
||||||
|
for ( scene in scenes ) {
|
||||||
|
val colors =
|
||||||
|
if ( scene == currentScene ) ButtonDefaults . buttonColors ()
|
||||||
|
else ButtonDefaults . filledTonalButtonColors ()
|
||||||
|
|
||||||
|
Button ( { onSwitch (scene) } , Modifier , true , ButtonDefaults . shape , colors ) {
|
||||||
|
Text ( scene . value )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSceneChange ( newScene : Scene ) {
|
||||||
|
for ( ( device , newState ) in newScene . states ) {
|
||||||
|
val jsonString = when (device) {
|
||||||
|
is TemperatureLamp -> Json . encodeToString ( newState as TemperatureLampState )
|
||||||
|
is RgbLamp -> Json . encodeToString ( newState as RgbLampState )
|
||||||
|
else -> throw Error ("Unknown type of device state")
|
||||||
|
}
|
||||||
|
MqttClient . publish (
|
||||||
|
Config . MQTT_TOPIC + "/" + device . name . value + "/set" ,
|
||||||
|
jsonString . toByteArray () . asUByteArray () ,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//private fun handlePowerChange ( deviceName : String , power : Power ) {
|
||||||
|
// when ( val device = AppState . devices . value . getValue (deviceName) ) {
|
||||||
|
//
|
||||||
|
// is TemperatureLamp ->
|
||||||
|
// if ( device . currentState != null && device . currentState . power != power ) {
|
||||||
|
// MqttClient . publish (
|
||||||
|
// "zigbee2mqtt/$deviceName/set/state" ,
|
||||||
|
// power . toString () . toByteArray () . asUByteArray () ,
|
||||||
|
// )
|
||||||
|
// AppState . putDevice ( TemperatureLamp ( deviceName , device . currentState . copy (power) ) )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// is RgbLamp ->
|
||||||
|
// if ( device . currentState != null && device . currentState . power != power ) {
|
||||||
|
// MqttClient . publish (
|
||||||
|
// "zigbee2mqtt/$deviceName/set/state" ,
|
||||||
|
// power . toString () . toByteArray () . asUByteArray () ,
|
||||||
|
// )
|
||||||
|
// AppState . putDevice ( RgbLamp ( deviceName , device . currentState . copy (power) ) )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
63
app/src/main/java/com/kernelmaft/zanbur/mqtt.kt
Normal file
63
app/src/main/java/com/kernelmaft/zanbur/mqtt.kt
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import MQTTClient
|
||||||
|
import mqtt.MQTTVersion
|
||||||
|
import mqtt.Subscription
|
||||||
|
import mqtt.packets.Qos.AT_MOST_ONCE
|
||||||
|
import mqtt.packets.mqtt.MQTTPublish
|
||||||
|
|
||||||
|
|
||||||
|
typealias MqttPublishHandler = ( MQTTPublish , Json ) -> Unit
|
||||||
|
|
||||||
|
object MqttClient {
|
||||||
|
private var client : MQTTClient ? = null
|
||||||
|
private var coroutineScope : CoroutineScope ? = null
|
||||||
|
private val publishHandlers : MutableList <MqttPublishHandler> = mutableListOf ()
|
||||||
|
|
||||||
|
fun run ( coroutineScope : CoroutineScope ) {
|
||||||
|
this . coroutineScope = coroutineScope
|
||||||
|
val json = Json { ignoreUnknownKeys = true }
|
||||||
|
|
||||||
|
coroutineScope . launch (IO) {
|
||||||
|
client = MQTTClient (
|
||||||
|
MQTTVersion . MQTT5 ,
|
||||||
|
Config . MQTT_SERVER_HOST ,
|
||||||
|
Config . MQTT_SERVER_PORT ,
|
||||||
|
null ,
|
||||||
|
) {
|
||||||
|
for ( handler in publishHandlers ) handler ( it , json )
|
||||||
|
}
|
||||||
|
client !! . subscribe ( listOf ( Subscription ( Config . MQTT_TOPIC + "/#" ) ) )
|
||||||
|
|
||||||
|
for ( ( name , device ) in AppState . devices . value ) when (device) {
|
||||||
|
is TemperatureLamp -> publish (
|
||||||
|
Config . MQTT_TOPIC + "/" + name + "/get" ,
|
||||||
|
Json . encodeToString ( TemperatureLampState ( Power . OFF , 0 , 250 ) )
|
||||||
|
. toByteArray ()
|
||||||
|
. asUByteArray () ,
|
||||||
|
)
|
||||||
|
is RgbLamp -> publish (
|
||||||
|
Config . MQTT_TOPIC + "/" + name + "/get" ,
|
||||||
|
Json . encodeToString ( RgbLampState ( Power . OFF , 0 , Cie1931Color ( 0.0 , 0.0 ) ) )
|
||||||
|
. toByteArray ()
|
||||||
|
. asUByteArray () ,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
client !! . run ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addPublishHandler ( handler : MqttPublishHandler ) = publishHandlers . add (handler)
|
||||||
|
|
||||||
|
fun publish ( topic : String , payload : UByteArray ) {
|
||||||
|
coroutineScope !! . launch (IO) {
|
||||||
|
client !! . publish ( false , AT_MOST_ONCE , topic , payload )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
app/src/main/java/com/kernelmaft/zanbur/ontology.kt
Normal file
62
app/src/main/java/com/kernelmaft/zanbur/ontology.kt
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
||||||
|
typealias Real = Double
|
||||||
|
|
||||||
|
|
||||||
|
data class Scene (
|
||||||
|
val name : SceneName ,
|
||||||
|
val states : Map < Device , LampState > ,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SceneName ( val value : String )
|
||||||
|
|
||||||
|
|
||||||
|
abstract class Device ( val name : DeviceName )
|
||||||
|
|
||||||
|
class TemperatureLamp ( name : DeviceName ) : Device (name)
|
||||||
|
class RgbLamp ( name : DeviceName ) : Device (name)
|
||||||
|
|
||||||
|
data class DeviceName ( val value : String )
|
||||||
|
|
||||||
|
|
||||||
|
abstract class LampState {
|
||||||
|
abstract val power : Power
|
||||||
|
abstract val brightness : Int // Range 0 to 254
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable data class TemperatureLampState (
|
||||||
|
@SerialName ("state") override val power : Power ,
|
||||||
|
@SerialName ("brightness") override val brightness : Int ,
|
||||||
|
@SerialName ("color_temp") val colorTemperature : Int , // Range 250 to 454
|
||||||
|
) : LampState ()
|
||||||
|
|
||||||
|
@Serializable data class RgbLampState (
|
||||||
|
@SerialName ("state") override val power : Power ,
|
||||||
|
@SerialName ("brightness") override val brightness : Int ,
|
||||||
|
@SerialName ("color") val color : Cie1931Color ,
|
||||||
|
) : LampState ()
|
||||||
|
|
||||||
|
@Serializable enum class Power {
|
||||||
|
ON , OFF ;
|
||||||
|
|
||||||
|
fun toBoolean () : Boolean = when (this) {
|
||||||
|
ON -> true
|
||||||
|
OFF -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromBoolean ( power : Boolean ) : Power = when (power) {
|
||||||
|
true -> ON
|
||||||
|
false -> OFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable data class Cie1931Color (
|
||||||
|
val x : Real , // Range 0.0 to 1.0 (for shrimp anyway)
|
||||||
|
val y : Real , // Idem
|
||||||
|
)
|
26
app/src/main/java/com/kernelmaft/zanbur/theme.kt
Normal file
26
app/src/main/java/com/kernelmaft/zanbur/theme.kt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package com.kernelmaft.zanbur
|
||||||
|
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.MaterialTheme.shapes
|
||||||
|
import androidx.compose.material3.MaterialTheme.typography
|
||||||
|
import androidx.compose.material3.dynamicDarkColorScheme
|
||||||
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
|
||||||
|
val compactSpacing = 16 . dp
|
||||||
|
|
||||||
|
@Composable fun ZanburTheme ( content : @Composable () -> Unit ) {
|
||||||
|
val colorScheme = run {
|
||||||
|
val context = LocalContext . current
|
||||||
|
if ( isSystemInDarkTheme () )
|
||||||
|
dynamicDarkColorScheme (context)
|
||||||
|
else
|
||||||
|
dynamicLightColorScheme (context)
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTheme ( colorScheme, shapes , typography , content )
|
||||||
|
}
|
170
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
170
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
30
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
30
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
6
app/src/main/res/mipmap-anydpi/ic_launcher.xml
Normal file
6
app/src/main/res/mipmap-anydpi/ic_launcher.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
6
app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
Normal file
6
app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
4
app/src/main/res/values/strings.xml
Normal file
4
app/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Zanbur</string>
|
||||||
|
<string name="title_activity_main">Zanbur</string>
|
||||||
|
</resources>
|
7
app/src/main/res/values/themes.xml
Normal file
7
app/src/main/res/values/themes.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<resources>
|
||||||
|
<style
|
||||||
|
name="Theme.Zanbur"
|
||||||
|
parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||||
|
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
2
build.gradle.kts
Normal file
2
build.gradle.kts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
plugins {}
|
25
gradle.properties
Normal file
25
gradle.properties
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Project-wide Gradle settings.
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -XX:+UseParallelGC
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. For more details, visit
|
||||||
|
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||||
|
org.gradle.parallel=true
|
||||||
|
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||||
|
# Android operating system, and which are packaged with your app's APK
|
||||||
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
|
android.useAndroidX=true
|
||||||
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
|
kotlin.code.style=official
|
||||||
|
# Enables namespacing of each library's R class so that its R class includes only the
|
||||||
|
# resources declared in the library itself and none from the library's dependencies,
|
||||||
|
# thereby reducing the size of the R class for that library
|
||||||
|
android.nonTransitiveRClass=true
|
||||||
|
|
||||||
|
org.gradle.configuration-cache=true
|
37
gradle/libs.versions.toml
Normal file
37
gradle/libs.versions.toml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[versions]
|
||||||
|
# Gradle plugins
|
||||||
|
android-plugin = "8.5.1"
|
||||||
|
kotlin = "2.0.0"
|
||||||
|
# Runtime libraries
|
||||||
|
androidx-core-ktx = "1.13.1"
|
||||||
|
android-material = "1.12.0"
|
||||||
|
androidx-compose-material3 = "1.2.1"
|
||||||
|
androidx-lifecycle-runtime-ktx = "2.8.3"
|
||||||
|
androidx-activity-compose = "1.9.0"
|
||||||
|
androidx-compose-ui = "1.6.8"
|
||||||
|
kotlinx-coroutines-android = "1.8.1"
|
||||||
|
kotlinx-serialization-json = "1.7.1"
|
||||||
|
# Other libraries
|
||||||
|
kmqtt = "0.4.8"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
# Runtime libraries
|
||||||
|
android-material = { group = "com.google.android.material", name = "material", version.ref = "android-material" }
|
||||||
|
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx-activity-compose" }
|
||||||
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" }
|
||||||
|
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "androidx-compose-material3" }
|
||||||
|
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "androidx-compose-ui" }
|
||||||
|
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics", version.ref = "androidx-compose-ui" }
|
||||||
|
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "androidx-compose-ui" }
|
||||||
|
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidx-lifecycle-runtime-ktx" }
|
||||||
|
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines-android" }
|
||||||
|
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
|
||||||
|
# Other libraries
|
||||||
|
kmqtt-common = { group = "io.github.davidepianca98", name = "kmqtt-common", version.ref = "kmqtt" }
|
||||||
|
kmqtt-client = { group = "io.github.davidepianca98", name = "kmqtt-client", version.ref = "kmqtt" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
android-application = { id = "com.android.application", version.ref = "android-plugin" }
|
||||||
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
|
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
23
settings.gradle.kts
Normal file
23
settings.gradle.kts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
google {
|
||||||
|
content {
|
||||||
|
includeGroupByRegex("com\\.android.*")
|
||||||
|
includeGroupByRegex("com\\.google.*")
|
||||||
|
includeGroupByRegex("androidx.*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = "Zanbur"
|
||||||
|
include(":app")
|
Loading…
Add table
Reference in a new issue