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/release/
|
||||
/.idea/
|
||||
/gradle/wrapper/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/keystore.properties
|
||||
/.kotlin/
|
||||
|
||||
# Android Studio defaults
|
||||
*.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