Initial public release.

This commit is contained in:
Joe Kearney 2026-03-01 17:03:10 -06:00
parent ed31acd60f
commit 58d87b11b7
249 changed files with 15831 additions and 4 deletions

85
app/build.gradle.kts Normal file
View file

@ -0,0 +1,85 @@
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
}
android {
namespace = "club.clubk.ktag.apps"
compileSdk = 36
defaultConfig {
applicationId = "club.clubk.ktag.apps"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
val gitHashProvider = providers.exec {
commandLine("git", "rev-parse", "--short", "HEAD")
}.standardOutput.asText
val gitHash = gitHashProvider.get().trim()
val buildTime = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US).format(Date())
buildConfigField("String", "GIT_HASH", "\"$gitHash\"")
buildConfigField("String", "BUILD_TIME", "\"$buildTime\"")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
buildFeatures {
buildConfig = true
compose = true
}
packaging {
resources {
excludes += listOf(
"META-INF/INDEX.LIST",
"META-INF/io.netty.versions.properties",
"META-INF/DEPENDENCIES"
)
}
}
}
dependencies {
implementation(project(":core"))
implementation(project(":subapp-sample"))
implementation(project(":subapp-bletool"))
implementation(project(":subapp-koth"))
implementation(project(":subapp-medic"))
implementation(project(":subapp-terminal"))
implementation(project(":subapp-mine"))
implementation(project(":subapp-konfigurator"))
implementation(project(":subapp-sentry"))
implementation(project(":mqtt-broker"))
implementation(project(":shared-services"))
implementation("androidx.appcompat:appcompat:1.7.0")
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.ui.tooling.preview)
debugImplementation(libs.androidx.compose.ui.tooling)
}

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.KTagApps">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".BleSettingsActivity"
android:exported="false"
android:parentActivityName=".MainActivity"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View file

@ -0,0 +1,14 @@
package club.clubk.ktag.apps
import android.content.Context
import android.content.Intent
import club.clubk.ktag.apps.sharedservices.BaseSettingsActivity
class BleSettingsActivity : BaseSettingsActivity() {
companion object {
fun createIntent(context: Context): Intent {
return BaseSettingsActivity.createIntent(context, R.xml.ble_settings_pref, BleSettingsActivity::class.java)
}
}
}

View file

@ -0,0 +1,303 @@
package club.clubk.ktag.apps
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import club.clubk.ktag.apps.core.SubApp
import club.clubk.ktag.apps.core.SubAppRegistry
import club.clubk.ktag.apps.core.ui.theme.KTagGreen
import club.clubk.ktag.apps.core.ui.theme.KTagRed
import club.clubk.ktag.apps.core.ui.theme.KTagYellow
import club.clubk.ktag.apps.mqttbroker.BrokerSettingsActivity
import club.clubk.ktag.apps.mqttbroker.BrokerStatus
import club.clubk.ktag.apps.mqttbroker.MqttBrokerManager
import club.clubk.ktag.apps.sharedservices.LocationPublisher
import club.clubk.ktag.apps.sharedservices.MqttClientStatus
import club.clubk.ktag.apps.sharedservices.SharedMqttClient
import club.clubk.ktag.apps.ui.theme.KTagAppsTheme
class MainActivity : ComponentActivity() {
private val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { startMqttServices() }
override fun onDestroy() {
super.onDestroy()
LocationPublisher.stop()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
KTagAppsTheme {
LauncherScreen()
}
}
val permissionsToRequest = buildList {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
ContextCompat.checkSelfPermission(this@MainActivity, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED
) {
add(Manifest.permission.POST_NOTIFICATIONS)
}
if (ContextCompat.checkSelfPermission(this@MainActivity, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
) {
add(Manifest.permission.ACCESS_FINE_LOCATION)
}
}
if (permissionsToRequest.isNotEmpty()) {
permissionLauncher.launch(permissionsToRequest.toTypedArray())
} else {
startMqttServices()
}
}
private fun startMqttServices() {
MqttBrokerManager.startBrokerIfEnabled(this)
SharedMqttClient.connect(this)
LocationPublisher.start(this)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LauncherScreen() {
val subApps = SubAppRegistry.getAll()
val context = LocalContext.current
var menuExpanded by remember { mutableStateOf(false) }
var showAboutDialog by remember { mutableStateOf(false) }
val brokerStatus by MqttBrokerManager.status.collectAsState()
val brokerEnabled = remember { MqttBrokerManager.isBrokerEnabled(context) }
val clientStatus by SharedMqttClient.status.collectAsState()
val deviceId = SharedMqttClient.currentDeviceId
Scaffold(
topBar = {
Column {
TopAppBar(
title = { Text("KTag Apps") },
actions = {
IconButton(onClick = { menuExpanded = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "Menu")
}
DropdownMenu(
expanded = menuExpanded,
onDismissRequest = { menuExpanded = false }
) {
DropdownMenuItem(
text = { Text("MQTT Settings") },
onClick = {
menuExpanded = false
context.startActivity(BrokerSettingsActivity.createIntent(context))
}
)
DropdownMenuItem(
text = { Text("BLE Settings") },
onClick = {
menuExpanded = false
context.startActivity(BleSettingsActivity.createIntent(context))
}
)
DropdownMenuItem(
text = { Text("About") },
onClick = {
menuExpanded = false
showAboutDialog = true
}
)
}
}
)
if (brokerEnabled) {
BrokerStatusBar(brokerStatus)
}
ClientStatusBar(clientStatus, deviceId)
}
}
) { padding ->
if (showAboutDialog) {
AboutDialog(onDismiss = { showAboutDialog = false })
}
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 100.dp),
modifier = Modifier
.fillMaxSize()
.padding(padding),
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
items(subApps) { subApp ->
SubAppItem(subApp)
}
}
}
}
@Composable
fun AboutDialog(onDismiss: () -> Unit) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text("KTag Apps") },
text = {
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text("Version ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
Text("Commit ${BuildConfig.GIT_HASH}")
Text("Built ${BuildConfig.BUILD_TIME}")
Text("Type ${BuildConfig.BUILD_TYPE}")
}
},
confirmButton = {
TextButton(onClick = onDismiss) { Text("OK") }
}
)
}
@Composable
fun BrokerStatusBar(status: BrokerStatus) {
val context = LocalContext.current
val (dotColor, statusText) = when (status) {
is BrokerStatus.Starting -> KTagYellow to "MQTT Broker starting\u2026"
is BrokerStatus.Running -> {
val ip = MqttBrokerManager.getDeviceIpAddress(context) ?: "no network"
val ports = if (status.sslPort != null) {
"$ip:${status.port} / SSL :${status.sslPort}"
} else {
"$ip:${status.port}"
}
KTagGreen to "MQTT Broker running \u00b7 $ports"
}
is BrokerStatus.Error -> KTagRed to "MQTT Broker error: ${status.message}"
is BrokerStatus.Stopped -> KTagRed to "MQTT Broker stopped"
}
Row(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceVariant)
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Box(
modifier = Modifier
.size(8.dp)
.clip(CircleShape)
.background(dotColor)
)
Text(
text = statusText,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@Composable
fun ClientStatusBar(status: MqttClientStatus, deviceId: String) {
val (dotColor, statusText) = when (status) {
is MqttClientStatus.Connecting -> KTagYellow to "MQTT Client connecting\u2026"
is MqttClientStatus.Connected -> KTagGreen to "MQTT Client connected as $deviceId"
is MqttClientStatus.Error -> KTagRed to "MQTT Client error: ${status.message}"
is MqttClientStatus.Disconnected -> KTagRed to "MQTT Client disconnected"
}
Row(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceVariant)
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Box(
modifier = Modifier
.size(8.dp)
.clip(CircleShape)
.background(dotColor)
)
Text(
text = statusText,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@Composable
fun SubAppItem(subApp: SubApp) {
val context = LocalContext.current
Column(
modifier = Modifier
.clickable { context.startActivity(subApp.createIntent(context)) }
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(
painter = painterResource(id = subApp.icon),
contentDescription = subApp.name,
modifier = Modifier.size(48.dp)
)
Text(
text = subApp.name,
style = MaterialTheme.typography.bodySmall,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 4.dp)
)
}
}

View file

@ -0,0 +1,18 @@
@file:Suppress("unused")
package club.clubk.ktag.apps.ui.theme
// Re-export core colors for use in this module
import club.clubk.ktag.apps.core.ui.theme.KTagBlue
import club.clubk.ktag.apps.core.ui.theme.KTagDarkGray
import club.clubk.ktag.apps.core.ui.theme.KTagGreen
import club.clubk.ktag.apps.core.ui.theme.KTagPurple
import club.clubk.ktag.apps.core.ui.theme.KTagRed
import club.clubk.ktag.apps.core.ui.theme.KTagYellow
// App color aliases for clarity in module-specific code
val AppGreen = KTagGreen
val AppBlue = KTagBlue
val AppRed = KTagRed
val AppYellow = KTagYellow
val AppPurple = KTagPurple
val AppDarkGray = KTagDarkGray

View file

@ -0,0 +1,52 @@
package club.clubk.ktag.apps.ui.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import club.clubk.ktag.apps.core.ui.theme.KTagDarkGray
import club.clubk.ktag.apps.core.ui.theme.KTagGreen
import club.clubk.ktag.apps.core.ui.theme.KTagPurple
import club.clubk.ktag.apps.core.ui.theme.KTagYellow
private val DarkColorScheme = darkColorScheme(
primary = KTagYellow,
onPrimary = KTagDarkGray,
secondary = KTagGreen,
onSecondary = KTagDarkGray,
tertiary = KTagPurple
)
private val LightColorScheme = lightColorScheme(
primary = KTagYellow,
onPrimary = KTagDarkGray,
secondary = KTagGreen,
onSecondary = KTagDarkGray,
tertiary = KTagPurple
)
@Composable
fun KTagAppsTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = false,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
content = content
)
}

View file

@ -0,0 +1,47 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="1700.8"
android:viewportHeight="1700.8">
<group android:scaleX="0.5"
android:scaleY="0.5"
android:translateX="425.2"
android:translateY="425.2">
<path
android:pathData="m852.8,90.8h5.6l24,12.1q24,12.1 31.7,15.8 7.7,3.7 52.6,24.4 44.8,20.6 46.3,20.8 1.5,0.2 23.8,9.8 22.3,9.6 34.3,14.2 12,4.6 34.8,12.5 22.7,7.9 28.7,10 6,2.1 15,5 9,2.9 23.2,7.1 14.2,4.2 36,10 21.9,5.8 42.5,10.4 20.6,4.6 26.6,5.8 6,1.2 38.2,7.1 32.2,5.8 40.3,7.1 8.1,1.3 39.9,6.2 31.8,5 32.2,5 0.4,0 30.9,5.8 30.5,5.8 30.7,6l0.2,0.2 -1.3,62.1q-1.3,62.1 -6.4,160.4 -5.2,98.3 -8.1,137.5 -3,39.2 -6.9,67.1 -3.9,27.9 -9,52.9 -5.1,25 -11.2,50.4 -6,25.4 -10.7,44.6 -4.7,19.2 -11.2,41.2 -6.4,22.1 -7.7,26.7 -1.3,4.6 -3.9,11.7 -2.6,7.1 -6,17.5 -3.4,10.4 -10.7,30 -7.3,19.6 -12,31.2 -4.7,11.7 -11.6,27.9 -6.9,16.3 -17.6,39.2 -10.7,22.9 -12.9,26.7 -2.1,3.8 -15.7,27.3 -13.5,23.5 -16.3,27.9 -2.8,4.4 -13.3,22.1 -10.5,17.7 -13.7,22.5 -3.2,4.8 -9.9,15 -6.7,10.2 -10.7,15.8 -4.1,5.6 -9.4,13.3 -5.4,7.7 -10.3,14.2 -4.9,6.5 -7.7,10.8 -2.8,4.4 -8.1,11.3 -5.4,6.9 -10.3,13.7 -4.9,6.9 -10.7,14.2 -5.8,7.3 -9.9,12.5 -4.1,5.2 -17.4,19.8 -13.3,14.6 -31.3,33.3 -18,18.7 -31.7,32.1 -13.7,13.3 -18.2,16.9 -4.5,3.5 -21,17.9 -16.5,14.4 -20.2,17.5 -3.6,3.1 -18,15.4 -14.4,12.3 -18.9,15.8 -4.5,3.5 -10.3,8.3 -5.8,4.8 -16.1,13.1 -10.3,8.3 -15.7,12.3 -5.4,4 -10.3,7.1 -4.9,3.1 -18,10.8 -13.1,7.7 -17.2,10.4 -4.1,2.7 -9.2,5.6 -5.1,2.9 -10.7,6.7 -5.6,3.8 -8.2,5.4 -2.6,1.7 -11.2,5.8 -8.6,4.2 -20.6,9.6 -12,5.4 -16.7,7.1l-4.7,1.7 -5.1,-1.3q-5.1,-1.3 -11.2,-4.2 -6,-2.9 -20.2,-10 -14.2,-7.1 -16.3,-8.8 -2.1,-1.7 -10.9,-7.3 -8.8,-5.6 -13.3,-8.3 -4.5,-2.7 -17.6,-10.4 -13.1,-7.7 -18,-10.8 -4.9,-3.1 -10.3,-7.1 -5.4,-4 -12.2,-9.4 -6.9,-5.4 -18.2,-14.8 -11.4,-9.4 -19.7,-16.2 -8.4,-6.9 -24.5,-20.8 -16.1,-13.9 -20.6,-17.5 -4.5,-3.5 -19.7,-16.7 -15.2,-13.1 -16.9,-14.8 -1.7,-1.7 -15.2,-14.8 -13.5,-13.1 -18.9,-18.7 -5.4,-5.6 -23.6,-25 -18.2,-19.4 -21.9,-23.7 -3.6,-4.4 -10.7,-12.9 -7.1,-8.5 -11.2,-13.8 -4.1,-5.2 -8.2,-10.8 -4.1,-5.6 -8.6,-11.7 -4.5,-6 -8.2,-10.8 -3.6,-4.8 -6.2,-8.5 -2.6,-3.8 -7.1,-9.8 -4.5,-6 -7.7,-10.8 -3.2,-4.8 -8.6,-12.5 -5.4,-7.7 -8.2,-12.1 -2.8,-4.4 -9,-13.8 -6.2,-9.4 -8.2,-12.9 -1.9,-3.5 -10.7,-17.9 -8.8,-14.4 -11.6,-18.7 -2.8,-4.4 -13.9,-24 -11.2,-19.6 -16.7,-29.6 -5.6,-10 -9.9,-18.7 -4.3,-8.8 -12,-26.2 -7.7,-17.5 -7.7,-17.9 0,-0.4 -7.7,-19.2 -7.7,-18.7 -12,-29.6 -4.3,-10.8 -12,-32.9 -7.7,-22.1 -15.4,-47.1 -7.7,-25 -17.2,-60.8 -9.4,-35.8 -13.3,-53.3 -3.9,-17.5 -10.7,-50.8 -6.9,-33.3 -8.2,-43.7 -1.3,-10.4 -5.6,-63.7 -4.3,-53.3 -5.6,-80.4 -1.3,-27.1 -3,-57.5 -1.7,-30.4 -4.7,-99.5 -3,-69.1 -3,-97.9v-28.7l2.8,-1.5q2.8,-1.5 15.2,-3.5 12.4,-2.1 34.3,-5.4 21.9,-3.3 66.5,-10.8 44.6,-7.5 63.9,-10.8 19.3,-3.3 51.1,-10.4 31.7,-7.1 54.9,-13.3 23.2,-6.2 39.9,-11.7 16.7,-5.4 26.2,-8.3 9.4,-2.9 18,-5.8 8.6,-2.9 34.3,-12.5 25.7,-9.6 48.3,-19.4 22.5,-9.8 24,-10 1.5,-0.2 21.7,-9.4 20.2,-9.2 36.5,-16.7 16.3,-7.5 32.2,-15 15.9,-7.5 39.5,-19.6l23.6,-12.1z"
android:strokeWidth="2"
android:fillColor="#FFC857"
android:strokeColor="#323031"
android:fillType="evenOdd"/>
<path
android:pathData="m831.2,104.9h5.6l24,12.1q24,12.1 31.7,15.8 7.7,3.7 52.6,24.4 44.8,20.6 46.3,20.8 1.5,0.2 23.8,9.8 22.3,9.6 34.3,14.2 12,4.6 34.7,12.5 22.7,7.9 28.8,10 6,2.1 15,5 9,2.9 23.2,7.1 14.2,4.2 36,10 21.9,5.8 42.5,10.4 20.6,4.6 26.6,5.8 6,1.2 38.2,7.1 32.2,5.8 40.3,7.1 8.1,1.2 39.9,6.2 31.8,5 32.2,5 0.4,0 30.9,5.8 30.5,5.8 30.7,6l0.2,0.2 -1.3,62.1q-1.3,62.1 -6.4,160.4 -5.2,98.3 -8.1,137.5 -3,39.2 -6.9,67.1 -3.9,27.9 -9,52.9 -5.2,25 -11.2,50.4 -6,25.4 -10.7,44.6 -4.7,19.2 -11.2,41.2 -6.4,22.1 -7.7,26.7 -1.3,4.6 -3.9,11.7 -2.6,7.1 -6,17.5 -3.4,10.4 -10.7,30 -7.3,19.6 -12,31.2 -4.7,11.7 -11.6,27.9 -6.9,16.2 -17.6,39.1 -10.7,22.9 -12.9,26.7 -2.1,3.8 -15.7,27.3 -13.5,23.5 -16.3,27.9 -2.8,4.4 -13.3,22.1 -10.5,17.7 -13.7,22.5 -3.2,4.8 -9.9,15 -6.7,10.2 -10.7,15.8 -4.1,5.6 -9.4,13.3 -5.4,7.7 -10.3,14.2 -4.9,6.5 -7.7,10.8 -2.8,4.4 -8.1,11.2 -5.4,6.9 -10.3,13.8 -4.9,6.9 -10.7,14.2 -5.8,7.3 -9.9,12.5 -4.1,5.2 -17.4,19.8 -13.3,14.6 -31.3,33.3 -18,18.8 -31.7,32.1 -13.7,13.3 -18.2,16.9 -4.5,3.5 -21,17.9 -16.5,14.4 -20.2,17.5 -3.6,3.1 -18,15.4 -14.4,12.3 -18.9,15.8 -4.5,3.5 -10.3,8.3 -5.8,4.8 -16.1,13.1 -10.3,8.3 -15.7,12.3 -5.4,4 -10.3,7.1 -4.9,3.1 -18,10.8 -13.1,7.7 -17.2,10.4 -4.1,2.7 -9.2,5.6 -5.1,2.9 -10.7,6.7 -5.6,3.7 -8.2,5.4 -2.6,1.7 -11.2,5.8 -8.6,4.2 -20.6,9.6 -12,5.4 -16.7,7.1l-4.7,1.7 -5.1,-1.3q-5.1,-1.3 -11.2,-4.2 -6,-2.9 -20.2,-10 -14.2,-7.1 -16.3,-8.8 -2.1,-1.7 -10.9,-7.3 -8.8,-5.6 -13.3,-8.3 -4.5,-2.7 -17.6,-10.4 -13.1,-7.7 -18,-10.8 -4.9,-3.1 -10.3,-7.1 -5.4,-4 -12.2,-9.4 -6.9,-5.4 -18.2,-14.8 -11.4,-9.4 -19.7,-16.2 -8.4,-6.9 -24.5,-20.8 -16.1,-13.9 -20.6,-17.5 -4.5,-3.5 -19.7,-16.7 -15.2,-13.1 -16.9,-14.8 -1.7,-1.7 -15.2,-14.8 -13.5,-13.1 -18.9,-18.7 -5.4,-5.6 -23.6,-25 -18.2,-19.4 -21.9,-23.7 -3.6,-4.4 -10.7,-12.9 -7.1,-8.5 -11.2,-13.7 -4.1,-5.2 -8.2,-10.8 -4.1,-5.6 -8.6,-11.7 -4.5,-6 -8.2,-10.8 -3.6,-4.8 -6.2,-8.5 -2.6,-3.8 -7.1,-9.8 -4.5,-6 -7.7,-10.8 -3.2,-4.8 -8.6,-12.5 -5.4,-7.7 -8.2,-12.1 -2.8,-4.4 -9,-13.8 -6.2,-9.4 -8.2,-12.9 -1.9,-3.5 -10.7,-17.9 -8.8,-14.4 -11.6,-18.7 -2.8,-4.4 -13.9,-24 -11.2,-19.6 -16.7,-29.6 -5.6,-10 -9.9,-18.7 -4.3,-8.8 -12,-26.2 -7.7,-17.5 -7.7,-17.9 0,-0.4 -7.7,-19.2 -7.7,-18.7 -12,-29.6 -4.3,-10.8 -12,-32.9 -7.7,-22.1 -15.4,-47.1 -7.7,-25 -17.2,-60.8 -9.4,-35.8 -13.3,-53.3 -3.9,-17.5 -10.7,-50.8 -6.9,-33.3 -8.2,-43.7 -1.3,-10.4 -5.6,-63.7 -4.3,-53.3 -5.6,-80.4 -1.3,-27.1 -3,-57.5 -1.7,-30.4 -4.7,-99.5 -3,-69.1 -3,-97.9L192.8,301.1l2.8,-1.5q2.8,-1.5 15.2,-3.5 12.4,-2.1 34.3,-5.4 21.9,-3.3 66.5,-10.8 44.6,-7.5 63.9,-10.8 19.3,-3.3 51.1,-10.4 31.7,-7.1 54.9,-13.3 23.2,-6.2 39.9,-11.7 16.7,-5.4 26.2,-8.3 9.4,-2.9 18,-5.8 8.6,-2.9 34.3,-12.5 25.7,-9.6 48.3,-19.4 22.5,-9.8 24,-10 1.5,-0.2 21.7,-9.4 20.2,-9.2 36.5,-16.7 16.3,-7.5 32.2,-15 15.9,-7.5 39.5,-19.6l23.6,-12.1z"
android:strokeWidth="2"
android:fillColor="#FFC857"
android:strokeColor="#323031"
android:fillType="evenOdd"/>
<path
android:pathData="m850.9,32.6h6l25.9,13q25.9,13 34.3,17.1 8.3,4 56.7,26.3 48.4,22.3 50,22.5 1.6,0.2 25.7,10.6 24.1,10.3 37,15.3 13,4.9 37.5,13.5 24.5,8.5 31,10.8 6.5,2.2 16.2,5.4 9.7,3.1 25,7.6 15.3,4.5 38.9,10.8 23.6,6.3 45.8,11.2 22.2,4.9 28.7,6.3 6.5,1.3 41.2,7.6 34.7,6.3 43.5,7.6 8.8,1.3 43.1,6.7 34.3,5.4 34.7,5.4 0.5,0 33.3,6.3 32.9,6.3 33.1,6.5l0.2,0.2 -1.4,67q-1.4,67 -6.9,173.1 -5.6,106.1 -8.8,148.4 -3.2,42.3 -7.4,72.4 -4.2,30.1 -9.7,57.1 -5.6,27 -12,54.4 -6.5,27.4 -11.6,48.1 -5.1,20.7 -12,44.5 -6.9,23.8 -8.3,28.8 -1.4,4.9 -4.2,12.6 -2.8,7.6 -6.5,18.9 -3.7,11.2 -11.6,32.4 -7.9,21.1 -13,33.7 -5.1,12.6 -12.5,30.1 -7.4,17.5 -19,42.3 -11.6,24.7 -13.9,28.8 -2.3,4 -16.9,29.5 -14.6,25.4 -17.6,30.1 -3,4.7 -14.4,23.8 -11.3,19.1 -14.8,24.3 -3.5,5.2 -10.6,16.2 -7.2,11 -11.6,17.1 -4.4,6.1 -10.2,14.4 -5.8,8.3 -11.1,15.3 -5.3,7 -8.3,11.7 -3,4.7 -8.8,12.1 -5.8,7.4 -11.1,14.8 -5.3,7.4 -11.6,15.3 -6.3,7.9 -10.6,13.5 -4.4,5.6 -18.8,21.4 -14.4,15.7 -33.8,36 -19.5,20.2 -34.3,34.6 -14.8,14.4 -19.7,18.2 -4.9,3.8 -22.7,19.3 -17.8,15.5 -21.8,18.9 -3.9,3.4 -19.5,16.6 -15.5,13.3 -20.4,17.1 -4.9,3.8 -11.1,9 -6.3,5.2 -17.4,14.2 -11.1,9 -16.9,13.3 -5.8,4.3 -11.1,7.6 -5.3,3.4 -19.4,11.7 -14.1,8.3 -18.5,11.2 -4.4,2.9 -10,6.1 -5.6,3.2 -11.6,7.2 -6,4.1 -8.8,5.8 -2.8,1.8 -12,6.3 -9.3,4.5 -22.2,10.3 -13,5.8 -18.1,7.7l-5.1,1.8 -5.6,-1.3q-5.6,-1.3 -12,-4.5 -6.5,-3.2 -21.8,-10.8 -15.3,-7.6 -17.6,-9.4 -2.3,-1.8 -11.8,-7.9 -9.5,-6.1 -14.4,-9 -4.9,-2.9 -19,-11.2 -14.1,-8.3 -19.4,-11.7 -5.3,-3.4 -11.1,-7.6 -5.8,-4.3 -13.2,-10.1 -7.4,-5.8 -19.7,-16 -12.3,-10.1 -21.3,-17.5 -9,-7.4 -26.4,-22.5 -17.4,-15.1 -22.2,-18.9 -4.9,-3.8 -21.3,-18 -16.4,-14.2 -18.3,-16 -1.9,-1.8 -16.4,-16 -14.6,-14.2 -20.4,-20.2 -5.8,-6.1 -25.5,-27 -19.7,-20.9 -23.6,-25.6 -3.9,-4.7 -11.6,-13.9 -7.6,-9.2 -12,-14.8 -4.4,-5.6 -8.8,-11.7 -4.4,-6.1 -9.3,-12.6 -4.9,-6.5 -8.8,-11.7 -3.9,-5.2 -6.7,-9.2 -2.8,-4.1 -7.6,-10.6 -4.9,-6.5 -8.3,-11.7 -3.5,-5.2 -9.3,-13.5 -5.8,-8.3 -8.8,-13 -3,-4.7 -9.7,-14.8 -6.7,-10.1 -8.8,-13.9 -2.1,-3.8 -11.6,-19.3 -9.5,-15.5 -12.5,-20.2 -3,-4.7 -15,-25.8 -12,-21.1 -18.1,-31.9 -6,-10.8 -10.6,-20.2 -4.6,-9.4 -13,-28.3 -8.3,-18.9 -8.3,-19.3 0,-0.4 -8.3,-20.7 -8.3,-20.2 -13,-31.9 -4.6,-11.7 -13,-35.5 -8.3,-23.8 -16.7,-50.8 -8.3,-27 -18.5,-65.6 -10.2,-38.7 -14.4,-57.5 -4.2,-18.9 -11.6,-54.8 -7.4,-36 -8.8,-47.2 -1.4,-11.2 -6,-68.8 -4.6,-57.5 -6,-86.8 -1.4,-29.2 -3.2,-62 -1.9,-32.8 -5.1,-107.5 -3.2,-74.6 -3.2,-105.7v-31l3,-1.6q3,-1.6 16.4,-3.8 13.4,-2.2 37,-5.8 23.6,-3.6 71.8,-11.7 48.2,-8.1 69,-11.7 20.8,-3.6 55.1,-11.2 34.3,-7.6 59.3,-14.4 25,-6.7 43.1,-12.6 18.1,-5.8 28.2,-9 10.2,-3.1 19.4,-6.3 9.3,-3.1 37,-13.5 27.8,-10.3 52.1,-20.9 24.3,-10.6 25.9,-10.8 1.6,-0.2 23.4,-10.1 21.8,-9.9 39.4,-18 17.6,-8.1 34.7,-16.2 17.1,-8.1 42.6,-21.1l25.5,-13z"
android:strokeWidth="2"
android:fillColor="#323031"
android:strokeColor="#323031"
android:fillType="evenOdd"/>
<path
android:pathData="m853.4,97.5h5.6l24,12.1q24,12.1 31.7,15.8 7.7,3.7 52.6,24.4 44.8,20.6 46.3,20.8 1.5,0.2 23.8,9.8 22.3,9.6 34.3,14.2 12,4.6 34.8,12.5 22.7,7.9 28.7,10 6,2.1 15,5 9,2.9 23.2,7.1 14.2,4.2 36,10 21.9,5.8 42.5,10.4 20.6,4.6 26.6,5.8 6,1.2 38.2,7.1 32.2,5.8 40.3,7.1 8.1,1.3 39.9,6.2 31.8,5 32.2,5 0.4,0 30.9,5.8 30.5,5.8 30.7,6l0.2,0.2 -1.3,62.1q-1.3,62.1 -6.4,160.4 -5.2,98.3 -8.1,137.5 -3,39.2 -6.9,67.1 -3.9,27.9 -9,52.9 -5.1,25 -11.2,50.4 -6,25.4 -10.7,44.6 -4.7,19.2 -11.2,41.2 -6.4,22.1 -7.7,26.7 -1.3,4.6 -3.9,11.7 -2.6,7.1 -6,17.5 -3.4,10.4 -10.7,30 -7.3,19.6 -12,31.2 -4.7,11.7 -11.6,27.9 -6.9,16.3 -17.6,39.2 -10.7,22.9 -12.9,26.6 -2.1,3.8 -15.7,27.3 -13.5,23.5 -16.3,27.9 -2.8,4.4 -13.3,22.1 -10.5,17.7 -13.7,22.5 -3.2,4.8 -9.9,15 -6.6,10.2 -10.7,15.8 -4.1,5.6 -9.4,13.3 -5.4,7.7 -10.3,14.2 -4.9,6.5 -7.7,10.8 -2.8,4.4 -8.1,11.3 -5.4,6.9 -10.3,13.7 -4.9,6.9 -10.7,14.2 -5.8,7.3 -9.9,12.5 -4.1,5.2 -17.4,19.8 -13.3,14.6 -31.3,33.3 -18,18.7 -31.7,32.1 -13.7,13.3 -18.2,16.9 -4.5,3.5 -21,17.9 -16.5,14.4 -20.2,17.5 -3.6,3.1 -18,15.4 -14.4,12.3 -18.9,15.8 -4.5,3.5 -10.3,8.3 -5.8,4.8 -16.1,13.1 -10.3,8.3 -15.7,12.3 -5.4,4 -10.3,7.1 -4.9,3.1 -18,10.8 -13.1,7.7 -17.2,10.4 -4.1,2.7 -9.2,5.6 -5.1,2.9 -10.7,6.7 -5.6,3.8 -8.2,5.4 -2.6,1.7 -11.2,5.8 -8.6,4.2 -20.6,9.6 -12,5.4 -16.7,7.1l-4.7,1.7 -5.1,-1.3q-5.1,-1.3 -11.2,-4.2 -6,-2.9 -20.2,-10 -14.2,-7.1 -16.3,-8.8 -2.1,-1.7 -10.9,-7.3 -8.8,-5.6 -13.3,-8.3 -4.5,-2.7 -17.6,-10.4 -13.1,-7.7 -18,-10.8 -4.9,-3.1 -10.3,-7.1 -5.4,-4 -12.2,-9.4 -6.9,-5.4 -18.2,-14.8 -11.4,-9.4 -19.7,-16.2 -8.4,-6.9 -24.5,-20.8 -16.1,-14 -20.6,-17.5 -4.5,-3.5 -19.7,-16.7 -15.2,-13.1 -16.9,-14.8 -1.7,-1.7 -15.2,-14.8 -13.5,-13.1 -18.9,-18.7 -5.4,-5.6 -23.6,-25 -18.2,-19.4 -21.9,-23.7 -3.6,-4.4 -10.7,-12.9 -7.1,-8.5 -11.2,-13.8 -4.1,-5.2 -8.2,-10.8 -4.1,-5.6 -8.6,-11.7 -4.5,-6 -8.2,-10.8 -3.6,-4.8 -6.2,-8.5 -2.6,-3.8 -7.1,-9.8 -4.5,-6 -7.7,-10.8 -3.2,-4.8 -8.6,-12.5 -5.4,-7.7 -8.2,-12.1 -2.8,-4.4 -9,-13.8 -6.2,-9.4 -8.2,-12.9 -1.9,-3.5 -10.7,-17.9 -8.8,-14.4 -11.6,-18.7 -2.8,-4.4 -13.9,-24 -11.2,-19.6 -16.7,-29.6 -5.6,-10 -9.9,-18.8 -4.3,-8.7 -12,-26.2 -7.7,-17.5 -7.7,-17.9 0,-0.4 -7.7,-19.2 -7.7,-18.7 -12,-29.6 -4.3,-10.8 -12,-32.9 -7.7,-22.1 -15.4,-47.1 -7.7,-25 -17.2,-60.8 -9.4,-35.8 -13.3,-53.3 -3.9,-17.5 -10.7,-50.8 -6.9,-33.3 -8.2,-43.7 -1.3,-10.4 -5.6,-63.7 -4.3,-53.3 -5.6,-80.4 -1.3,-27.1 -3,-57.5 -1.7,-30.4 -4.7,-99.5 -3,-69.1 -3,-97.9v-28.7l2.8,-1.5q2.8,-1.5 15.2,-3.5 12.4,-2.1 34.3,-5.4 21.9,-3.3 66.5,-10.8 44.6,-7.5 63.9,-10.8 19.3,-3.3 51.1,-10.4 31.7,-7.1 54.9,-13.3 23.2,-6.2 39.9,-11.7 16.7,-5.4 26.2,-8.3 9.4,-2.9 18,-5.8 8.6,-2.9 34.3,-12.5 25.7,-9.6 48.3,-19.4 22.5,-9.8 24,-10 1.5,-0.2 21.7,-9.4 20.2,-9.2 36.5,-16.7 16.3,-7.5 32.2,-15 15.9,-7.5 39.5,-19.6l23.6,-12.1z"
android:strokeWidth="2"
android:fillColor="#ffc857"
android:strokeColor="#323031"
android:fillType="evenOdd"/>
<path
android:pathData="M528.6,300.7L705.8,300.7L705.8,646.6L961.3,300.7L1167,300.7L836.1,749.5 1201,1248.4l-221.8,0L705.8,875.2L705.8,1248.4L528.6,1248.4Z"
android:fillColor="#323031"/>
<path
android:pathData="m555.2,1216.9l0,-101.9l23.6,-0l0,36.7l97.6,-0l0,28.5l-97.6,-0l0,36.7z"
android:fillColor="#ffc857"/>
<path
android:pathData="m654.3,1044.9l0,44.6l22.1,7l0,28.7l-121.2,-40.9l0,-34l121.2,-40.9l0,28.7zM631.8,1082.4l0,-30.3l-48.2,15.1z"
android:fillColor="#ffc857"/>
<path
android:pathData="m667.4,895.3q5.7,10.7 8.5,22.1 2.8,11.5 2.8,23.7 0,27.6 -16.9,43.8 -17,16.1 -45.9,16.1 -29.3,-0 -46.1,-16.4 -16.8,-16.4 -16.8,-45 0,-11 2.3,-21.1 2.3,-10.1 6.7,-19.1l25.1,-0q-5.8,9.3 -8.6,18.4 -2.8,9.1 -2.8,18.3 0,17 10.5,26.3 10.4,9.2 29.8,9.2 19.2,-0 29.7,-8.9 10.5,-8.9 10.5,-25.3 0,-4.4 -0.6,-8.2 -0.6,-3.9 -1.9,-6.9l-23.5,-0l0,17.4l-20.9,-0l0,-44.4z"
android:fillColor="#ffc857"/>
</group>
</vector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">KTag Apps</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.KTagApps" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory android:title="Device Detection">
<club.clubk.ktag.apps.sharedservices.SummarizedEditTextPreference
app:customHint="Seconds before a device is removed from the list"
android:inputType="number"
android:key="device_ttl_s"
android:summary="%s seconds"
android:defaultValue="5"
android:title="Device Timeout" />
</PreferenceCategory>
</PreferenceScreen>