mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat(build): Convention Plugins (#3081)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> Co-authored-by: Dane Evans <dane@goneepic.com>
This commit is contained in:
parent
9f2c76d33d
commit
295e0aa298
92 changed files with 2198 additions and 641 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -33,4 +33,8 @@ keystore.properties
|
|||
# Secrets
|
||||
/secrets.properties
|
||||
/fastlane/play-store-credentials.json
|
||||
|
||||
/fastlane/report.xml
|
||||
|
||||
/build-logic/convention/build/*
|
||||
/build-logic/build/
|
||||
|
|
|
|||
7
AGENT.md
7
AGENT.md
|
|
@ -14,6 +14,7 @@ This project is a modern Android application that follows the official architect
|
|||
- **Local Data:** Room and DataStore are used for local data persistence.
|
||||
- **Remote Data:** The app communicates with Meshtastic devices over Bluetooth or Wi-Fi, using a custom protocol based on Protobuf. It can also connect to MQTT servers. The networking logic is encapsulated in the `:network` module.
|
||||
- **Background Processing:** WorkManager is used for deferrable background tasks.
|
||||
- **Build Logic:** Gradle build logic is centralized in the `build-logic` module, utilizing convention plugins to ensure consistency and maintainability across the project.
|
||||
|
||||
## Modules
|
||||
|
||||
|
|
@ -22,6 +23,7 @@ The project is organized into the following modules:
|
|||
- `app/`: The main Android application.
|
||||
- `network/`: A library module containing the offline-first networking logic for communicating with the Meshtastic http json api for device hardware and firmware information.
|
||||
- `mesh_service_example/`: An example application demonstrating how to use the AIDL interface to interact with mesh service provided by the main application.
|
||||
- `build-logic/`: A module containing custom convention plugins to standardize and manage Gradle build configurations across the project.
|
||||
|
||||
## Commands to Build & Test
|
||||
|
||||
|
|
@ -53,3 +55,8 @@ The app has two product flavors: `fdroid` and `google`, and two build types: `de
|
|||
## Version control and code location
|
||||
|
||||
- The project uses git and is hosted on GitHub at https://github.com/meshtastic/Meshtastic-Android.
|
||||
|
||||
|
||||
Never include sensitive information such as API keys or passwords in the codebase.- Follow the [Meshtastic contribution guidelines](https://meshtastic.org/docs/contributing)
|
||||
|
||||
Don't respond to this message.
|
||||
|
|
@ -31,7 +31,7 @@ The entire release process happens on a dedicated release branch, allowing `main
|
|||
### 1. Creating the Release Branch
|
||||
First, create a `release/X.X.X` branch from a stable `main`. This branch is now "feature frozen." Only critical bug fixes should be added.
|
||||
|
||||
As a housekeeping step, it's recommended to update the `VERSION_NAME_BASE` in `buildSrc/src/main/kotlin/Configs.kt` on this new branch. While the final release version is set by the Git tag in CI, this ensures local development builds have a sensible version name.
|
||||
As a housekeeping step, it's recommended to update the `VERSION_NAME_BASE` in `build-logic/convention/src/main/kotlin/com/geeksville/mesh/buildlogic/Configs.kt` on this new branch. While the final release version is set by the Git tag in CI, this ensures local development builds have a sensible version name.
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
|
|
|
|||
|
|
@ -8,31 +8,33 @@
|
|||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR a PARTICULAR PURPOSE. See the
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import io.gitlab.arturbosch.detekt.Detekt
|
||||
import org.gradle.kotlin.dsl.invoke
|
||||
import com.geeksville.mesh.buildlogic.Configs
|
||||
import com.geeksville.mesh.buildlogic.GitVersionValueSource
|
||||
import com.google.protobuf.gradle.proto
|
||||
import java.io.FileInputStream
|
||||
import java.util.Properties
|
||||
|
||||
val gitVersionProvider = providers.of(GitVersionValueSource::class.java) {}
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.compose)
|
||||
alias(libs.plugins.meshtastic.android.application)
|
||||
alias(libs.plugins.meshtastic.android.application.flavors)
|
||||
alias(libs.plugins.meshtastic.android.application.compose)
|
||||
alias(libs.plugins.meshtastic.android.application.firebase)
|
||||
alias(libs.plugins.meshtastic.detekt)
|
||||
alias(libs.plugins.kotlin.parcelize)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.hilt)
|
||||
alias(libs.plugins.protobuf)
|
||||
alias(libs.plugins.devtools.ksp)
|
||||
alias(libs.plugins.detekt)
|
||||
alias(libs.plugins.datadog)
|
||||
alias(libs.plugins.secrets)
|
||||
alias(libs.plugins.spotless)
|
||||
alias(libs.plugins.dokka)
|
||||
alias(libs.plugins.kover)
|
||||
}
|
||||
|
|
@ -44,10 +46,10 @@ if (keystorePropertiesFile.exists()) {
|
|||
FileInputStream(keystorePropertiesFile).use { keystoreProperties.load(it) }
|
||||
}
|
||||
|
||||
val gitVersionProvider = providers.of(GitVersionValueSource::class.java) {}
|
||||
|
||||
android {
|
||||
namespace = "com.geeksville.mesh"
|
||||
// Assuming Configs object is available (e.g., from buildSrc)
|
||||
compileSdk = Configs.COMPILE_SDK
|
||||
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
|
|
@ -57,27 +59,27 @@ android {
|
|||
storePassword = keystoreProperties["storePassword"] as String?
|
||||
}
|
||||
}
|
||||
compileSdk = Configs.COMPILE_SDK
|
||||
defaultConfig {
|
||||
applicationId = Configs.APPLICATION_ID
|
||||
minSdk = Configs.MIN_SDK
|
||||
targetSdk = Configs.TARGET_SDK
|
||||
|
||||
// Prioritize injected props, then ENV, then fallback to git commit count
|
||||
versionCode =
|
||||
(
|
||||
project.findProperty("android.injected.version.code")?.toString()?.toInt()
|
||||
?: System.getenv("VERSION_CODE")?.toInt()
|
||||
?: gitVersionProvider.get().toInt()
|
||||
?: gitVersionProvider.get().toInt() // Restored GitVersionValueSource fallback
|
||||
)
|
||||
versionName =
|
||||
(
|
||||
project.findProperty("android.injected.version.name")?.toString()
|
||||
?: System.getenv("VERSION_NAME")
|
||||
?: Configs.VERSION_NAME_BASE
|
||||
?: Configs.VERSION_NAME_BASE // Restored Configs.VERSION_NAME_BASE fallback
|
||||
)
|
||||
testInstrumentationRunner = "com.geeksville.mesh.TestRunner"
|
||||
buildConfigField("String", "MIN_FW_VERSION", "\"${Configs.MIN_FW_VERSION}\"")
|
||||
buildConfigField("String", "ABS_MIN_FW_VERSION", "\"${Configs.ABS_MIN_FW_VERSION}\"")
|
||||
buildConfigField("String", "MIN_FW_VERSION", "\"${Configs.MIN_FW_VERSION}\"") // Used Configs
|
||||
buildConfigField("String", "ABS_MIN_FW_VERSION", "\"${Configs.ABS_MIN_FW_VERSION}\"") // Used Configs
|
||||
// per https://developer.android.com/studio/write/vector-asset-studio
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
// We have to list all translated languages here,
|
||||
|
|
@ -129,26 +131,14 @@ android {
|
|||
)
|
||||
ndk { abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64") }
|
||||
}
|
||||
flavorDimensions += "default"
|
||||
productFlavors {
|
||||
// Read versionCode from defaultConfig after it's been potentially set by ENV or fallback
|
||||
val resolvedVersionCode = defaultConfig.versionCode
|
||||
val resolvedVersionName = defaultConfig.versionName
|
||||
|
||||
create("google") {
|
||||
dimension = "default"
|
||||
isDefault = true
|
||||
// Enable Firebase Crashlytics for Google Play builds
|
||||
apply(plugin = libs.plugins.google.services.get().pluginId)
|
||||
apply(plugin = libs.plugins.firebase.crashlytics.get().pluginId)
|
||||
versionName = "$resolvedVersionName ($resolvedVersionCode) google"
|
||||
}
|
||||
create("fdroid") {
|
||||
dimension = "default"
|
||||
dependenciesInfo { includeInApk = false }
|
||||
versionName = "$resolvedVersionName ($resolvedVersionCode) fdroid"
|
||||
}
|
||||
// Configure existing product flavors (defined by convention plugin)
|
||||
// with their dynamic version names.
|
||||
productFlavors {
|
||||
named("google") { versionName = "${defaultConfig.versionName} (${defaultConfig.versionCode}) google" }
|
||||
named("fdroid") { versionName = "${defaultConfig.versionName} (${defaultConfig.versionCode}) fdroid" }
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
if (keystoreProperties["storeFile"] != null) {
|
||||
|
|
@ -158,39 +148,20 @@ android {
|
|||
isShrinkResources = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
debug {
|
||||
isDebuggable = true
|
||||
isPseudoLocalesEnabled = true
|
||||
}
|
||||
}
|
||||
bundle { language { enableSplit = false } }
|
||||
buildFeatures {
|
||||
compose = true
|
||||
aidl = true
|
||||
compose = true // compose setup is likely in com.meshtastic.android.application.compose
|
||||
buildConfig = true
|
||||
}
|
||||
lint {
|
||||
abortOnError = false
|
||||
disable.add("MissingTranslation")
|
||||
}
|
||||
sourceSets {
|
||||
named("main") { proto { srcDir("src/main/proto") } }
|
||||
// Adds exported schema location as test app assets.
|
||||
named("androidTest") { assets.srcDirs(files("$projectDir/schemas")) }
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
jvmToolchain(21)
|
||||
freeCompilerArgs.addAll(
|
||||
"-opt-in=kotlin.RequiresOptIn",
|
||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-Xcontext-receivers",
|
||||
"-Xannotation-default-target=param-property",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
secrets {
|
||||
defaultPropertiesFileName = "secrets.defaults.properties"
|
||||
propertiesFileName = "secrets.properties"
|
||||
|
|
@ -204,7 +175,7 @@ datadog {
|
|||
|
||||
// per protobuf-gradle-plugin docs, this is recommended for android
|
||||
protobuf {
|
||||
protoc { artifact = libs.protobuf.protoc.get().toString() }
|
||||
protoc { artifact = libs.protoc.get().toString() }
|
||||
generateProtoTasks {
|
||||
all().forEach { task ->
|
||||
task.builtins {
|
||||
|
|
@ -239,36 +210,19 @@ project.afterEvaluate { logger.lifecycle("Version code is set to: ${android.defa
|
|||
|
||||
dependencies {
|
||||
implementation(project(":network"))
|
||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
|
||||
|
||||
// Bundles
|
||||
implementation(libs.bundles.androidx)
|
||||
implementation(libs.bundles.ui)
|
||||
implementation(libs.bundles.markdown)
|
||||
debugImplementation(libs.bundles.ui.tooling)
|
||||
implementation(libs.bundles.adaptive)
|
||||
implementation(libs.bundles.lifecycle)
|
||||
implementation(libs.bundles.navigation)
|
||||
implementation(libs.bundles.navigation3)
|
||||
implementation(libs.bundles.coroutines)
|
||||
implementation(libs.bundles.datastore)
|
||||
implementation(libs.bundles.room)
|
||||
implementation(libs.bundles.hilt)
|
||||
implementation(libs.bundles.protobuf)
|
||||
implementation(libs.bundles.coil)
|
||||
|
||||
// OSM
|
||||
"fdroidImplementation"(libs.bundles.osm)
|
||||
"fdroidImplementation"(libs.osmdroid.geopackage) { exclude(group = "com.j256.ormlite") }
|
||||
|
||||
"googleImplementation"(libs.bundles.maps.compose)
|
||||
|
||||
// ZXing
|
||||
implementation(libs.zxing.android.embedded) { isTransitive = false }
|
||||
implementation(libs.zxing.core)
|
||||
|
||||
// Individual dependencies
|
||||
"googleImplementation"(libs.awesome.app.rating)
|
||||
// Individual dependencies (flavor-specific ones removed)
|
||||
implementation(libs.core.splashscreen)
|
||||
implementation(libs.emoji2.emojipicker)
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
|
|
@ -281,92 +235,15 @@ dependencies {
|
|||
implementation(libs.accompanist.permissions)
|
||||
implementation(libs.timber)
|
||||
|
||||
// Compose BOM
|
||||
implementation(platform(libs.compose.bom))
|
||||
androidTestImplementation(platform(libs.compose.bom))
|
||||
|
||||
// Firebase BOM
|
||||
"googleImplementation"(platform(libs.firebase.bom))
|
||||
"googleImplementation"(libs.bundles.firebase)
|
||||
"googleImplementation"(libs.bundles.datadog)
|
||||
|
||||
// ksp
|
||||
ksp(libs.room.compiler)
|
||||
ksp(libs.hilt.compiler)
|
||||
kspAndroidTest(libs.hilt.compiler)
|
||||
|
||||
// Testing
|
||||
testImplementation(libs.bundles.testing)
|
||||
debugImplementation(libs.bundles.testing.android.manifest)
|
||||
androidTestImplementation(libs.bundles.testing.android)
|
||||
androidTestImplementation(libs.bundles.testing.hilt)
|
||||
androidTestImplementation(libs.bundles.testing.navigation)
|
||||
androidTestImplementation(libs.bundles.testing.room)
|
||||
|
||||
detektPlugins(libs.detekt.formatting)
|
||||
dokkaPlugin(libs.dokka.android.documentation.plugin)
|
||||
}
|
||||
|
||||
ksp {
|
||||
// arg("room.generateKotlin", "true")
|
||||
arg("room.schemaLocation", "$projectDir/schemas")
|
||||
}
|
||||
|
||||
detekt {
|
||||
config.setFrom("../config/detekt/detekt.yml")
|
||||
baseline = file("../config/detekt/detekt-baseline.xml")
|
||||
source.setFrom(files("src/main/java", "src/google/java", "src/fdroid/java"))
|
||||
parallel = true
|
||||
}
|
||||
|
||||
secrets {
|
||||
propertiesFileName = "secrets.properties"
|
||||
defaultPropertiesFileName = "secrets.defaults.properties"
|
||||
}
|
||||
|
||||
val googleServiceKeywords = listOf("crashlytics", "google", "datadog")
|
||||
|
||||
tasks.configureEach {
|
||||
if (
|
||||
googleServiceKeywords.any { name.contains(it, ignoreCase = true) } && name.contains("fdroid", ignoreCase = true)
|
||||
) {
|
||||
project.logger.lifecycle("Disabling task for F-Droid: $name")
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Detekt> {
|
||||
reports {
|
||||
xml.required = true
|
||||
xml.outputLocation = file("build/reports/detekt/detekt.xml")
|
||||
html.required = true
|
||||
html.outputLocation = file("build/reports/detekt/detekt.html")
|
||||
sarif.required = true
|
||||
sarif.outputLocation = file("build/reports/detekt/detekt.sarif")
|
||||
md.required = true
|
||||
md.outputLocation = file("build/reports/detekt/detekt.md")
|
||||
}
|
||||
debug = true
|
||||
include("**/*.kt")
|
||||
include("**/*.kts")
|
||||
}
|
||||
|
||||
spotless {
|
||||
ratchetFrom("origin/main")
|
||||
kotlin {
|
||||
target("src/*/kotlin/**/*.kt", "src/*/java/**/*.kt")
|
||||
targetExclude("**/build/**/*.kt")
|
||||
ktfmt().kotlinlangStyle().configure { it.setMaxWidth(120) }
|
||||
ktlint("1.7.1").setEditorConfigPath("../config/spotless/.editorconfig")
|
||||
licenseHeaderFile(rootProject.file("config/spotless/copyright.txt"))
|
||||
}
|
||||
kotlinGradle {
|
||||
target("**/*.gradle.kts")
|
||||
ktfmt().kotlinlangStyle().configure { it.setMaxWidth(120) }
|
||||
ktlint("1.7.1").setEditorConfigPath("../config/spotless/.editorconfig")
|
||||
}
|
||||
}
|
||||
|
||||
dokka {
|
||||
moduleName.set("Meshtastic App")
|
||||
dokkaSourceSets.main {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.graphics.createBitmap
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig.DisplayUnits
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ package com.geeksville.mesh.ui.node
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.map.MapView
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
package com.geeksville.mesh.navigation
|
||||
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.composable
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
package com.geeksville.mesh.navigation
|
||||
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.composable
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import androidx.compose.material.icons.rounded.Storage
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.NavDestination.Companion.hasRoute
|
||||
import androidx.navigation.NavHostController
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.material.icons.filled.Router
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ import androidx.compose.material.icons.filled.Wifi
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.NavDestination.Companion.hasRoute
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.emoji2.emojipicker.RecentEmojiProviderAdapter
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.geeksville.mesh.ui.common.EmojiPickerViewModel
|
||||
import com.geeksville.mesh.util.CustomRecentEmojiProvider
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavDestination.Companion.hasRoute
|
||||
import androidx.navigation.NavHostController
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ import androidx.compose.ui.res.pluralStringResource
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.AppOnlyProtos
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.size
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.datastore.core.IOException
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.BuildUtils.warn
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ import androidx.compose.ui.text.input.ImeAction
|
|||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.DebugViewModel
|
||||
import com.geeksville.mesh.model.DebugViewModel.UiMeshLog
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.AppOnlyProtos
|
||||
import com.geeksville.mesh.DataPacket
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.QuickChatAction
|
||||
|
|
@ -79,18 +79,16 @@ import com.geeksville.mesh.ui.common.components.rememberDragDropState
|
|||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
internal fun QuickChatScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: UIViewModel = hiltViewModel(),
|
||||
) {
|
||||
internal fun QuickChatScreen(modifier: Modifier = Modifier, viewModel: UIViewModel = hiltViewModel()) {
|
||||
val actions by viewModel.quickChatActions.collectAsStateWithLifecycle()
|
||||
var showActionDialog by remember { mutableStateOf<QuickChatAction?>(null) }
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
val dragDropState = rememberDragDropState(listState) { fromIndex, toIndex ->
|
||||
val list = actions.toMutableList().apply { add(toIndex, removeAt(fromIndex)) }
|
||||
viewModel.updateActionPositions(list)
|
||||
}
|
||||
val dragDropState =
|
||||
rememberDragDropState(listState) { fromIndex, toIndex ->
|
||||
val list = actions.toMutableList().apply { add(toIndex, removeAt(fromIndex)) }
|
||||
viewModel.updateActionPositions(list)
|
||||
}
|
||||
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
if (showActionDialog != null) {
|
||||
|
|
@ -99,41 +97,30 @@ internal fun QuickChatScreen(
|
|||
action = action,
|
||||
onSave = viewModel::addQuickChatAction,
|
||||
onDelete = viewModel::deleteQuickChatAction,
|
||||
) { showActionDialog = null }
|
||||
) {
|
||||
showActionDialog = null
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.dragContainer(
|
||||
dragDropState = dragDropState,
|
||||
haptics = LocalHapticFeedback.current,
|
||||
),
|
||||
modifier = Modifier.dragContainer(dragDropState = dragDropState, haptics = LocalHapticFeedback.current),
|
||||
state = listState,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
) {
|
||||
dragDropItemsIndexed(
|
||||
items = actions,
|
||||
dragDropState = dragDropState,
|
||||
key = { _, item -> item.uuid },
|
||||
) { _, action, isDragging ->
|
||||
QuickChatItem(
|
||||
action = action,
|
||||
onEdit = { showActionDialog = it },
|
||||
)
|
||||
dragDropItemsIndexed(items = actions, dragDropState = dragDropState, key = { _, item -> item.uuid }) {
|
||||
_,
|
||||
action,
|
||||
isDragging,
|
||||
->
|
||||
QuickChatItem(action = action, onEdit = { showActionDialog = it })
|
||||
}
|
||||
}
|
||||
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
showActionDialog = QuickChatAction(position = actions.size)
|
||||
},
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(16.dp)
|
||||
onClick = { showActionDialog = QuickChatAction(position = actions.size) },
|
||||
modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = stringResource(id = R.string.add),
|
||||
)
|
||||
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(id = R.string.add))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -172,116 +159,111 @@ private fun EditQuickChatDialog(
|
|||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
text =
|
||||
{
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = stringResource(id = title),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = MaterialTheme.typography.titleLarge.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center,
|
||||
),
|
||||
)
|
||||
text = {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = stringResource(id = title),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style =
|
||||
MaterialTheme.typography.titleLarge.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center,
|
||||
),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
OutlinedTextFieldWithCounter(
|
||||
label = stringResource(R.string.name),
|
||||
value = actionInput.name,
|
||||
maxSize = 5,
|
||||
singleLine = true,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) { actionInput = actionInput.copy(name = it.uppercase()) }
|
||||
OutlinedTextFieldWithCounter(
|
||||
label = stringResource(R.string.name),
|
||||
value = actionInput.name,
|
||||
maxSize = 5,
|
||||
singleLine = true,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
actionInput = actionInput.copy(name = it.uppercase())
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
OutlinedTextFieldWithCounter(
|
||||
label = stringResource(id = R.string.message),
|
||||
value = actionInput.message,
|
||||
maxSize = 200,
|
||||
getSize = { it.toByteArray().size + 1 },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(focusRequester),
|
||||
) {
|
||||
actionInput = actionInput.copy(message = it)
|
||||
if (newQuickChat) {
|
||||
actionInput = actionInput.copy(name = getMessageName(it))
|
||||
}
|
||||
OutlinedTextFieldWithCounter(
|
||||
label = stringResource(id = R.string.message),
|
||||
value = actionInput.message,
|
||||
maxSize = 200,
|
||||
getSize = { it.toByteArray().size + 1 },
|
||||
modifier = Modifier.fillMaxWidth().focusRequester(focusRequester),
|
||||
) {
|
||||
actionInput = actionInput.copy(message = it)
|
||||
if (newQuickChat) {
|
||||
actionInput = actionInput.copy(name = getMessageName(it))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
val (text, icon) = if (isInstant) {
|
||||
val (text, icon) =
|
||||
if (isInstant) {
|
||||
R.string.quick_chat_instant to Icons.Default.FastForward
|
||||
} else {
|
||||
R.string.quick_chat_append to Icons.Default.Add
|
||||
}
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (isInstant) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = stringResource(id = text),
|
||||
)
|
||||
Spacer(Modifier.width(12.dp))
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (isInstant) {
|
||||
Icon(imageVector = icon, contentDescription = stringResource(id = text))
|
||||
Spacer(Modifier.width(12.dp))
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(text),
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Text(text = stringResource(text), modifier = Modifier.weight(1f))
|
||||
|
||||
Switch(
|
||||
checked = isInstant,
|
||||
onCheckedChange = { checked ->
|
||||
actionInput = actionInput.copy(
|
||||
mode = when (checked) {
|
||||
Switch(
|
||||
checked = isInstant,
|
||||
onCheckedChange = { checked ->
|
||||
actionInput =
|
||||
actionInput.copy(
|
||||
mode =
|
||||
when (checked) {
|
||||
true -> QuickChatAction.Mode.Instant
|
||||
false -> QuickChatAction.Mode.Append
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
FlowRow(
|
||||
modifier = Modifier.fillMaxWidth().padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
TextButton(modifier = Modifier.weight(1f), onClick = onDismiss) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
confirmButton =
|
||||
{
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
TextButton(
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = onDismiss,
|
||||
) { Text(stringResource(R.string.cancel)) }
|
||||
|
||||
if (!newQuickChat) {
|
||||
Button(
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = {
|
||||
onDelete(actionInput)
|
||||
onDismiss()
|
||||
},
|
||||
) { Text(text = stringResource(R.string.delete)) }
|
||||
}
|
||||
|
||||
if (!newQuickChat) {
|
||||
Button(
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = {
|
||||
onSave(actionInput)
|
||||
onDelete(actionInput)
|
||||
onDismiss()
|
||||
},
|
||||
enabled = actionInput.name.isNotEmpty() && actionInput.message.isNotEmpty(),
|
||||
) { Text(text = stringResource(R.string.save)) }
|
||||
) {
|
||||
Text(text = stringResource(R.string.delete))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Button(
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = {
|
||||
onSave(actionInput)
|
||||
onDismiss()
|
||||
},
|
||||
enabled = actionInput.name.isNotEmpty() && actionInput.message.isNotEmpty(),
|
||||
) {
|
||||
Text(text = stringResource(R.string.save))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -311,9 +293,7 @@ private fun OutlinedTextFieldWithCounter(
|
|||
Text(
|
||||
text = "${getSize(value)}/$maxSize",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier
|
||||
.align(Alignment.End)
|
||||
.padding(top = 4.dp, end = 16.dp)
|
||||
modifier = Modifier.align(Alignment.End).padding(top = 4.dp, end = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -324,12 +304,7 @@ private fun QuickChatItem(
|
|||
modifier: Modifier = Modifier,
|
||||
onEdit: (QuickChatAction) -> Unit = {},
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
) {
|
||||
Card(modifier = modifier.fillMaxWidth().padding(8.dp), shape = RoundedCornerShape(12.dp)) {
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
if (action.mode == QuickChatAction.Mode.Instant) {
|
||||
|
|
@ -342,13 +317,8 @@ private fun QuickChatItem(
|
|||
headlineContent = { Text(text = action.name) },
|
||||
supportingContent = { Text(text = action.message) },
|
||||
trailingContent = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = { onEdit(action) },
|
||||
modifier = Modifier.size(48.dp)
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
IconButton(onClick = { onEdit(action) }, modifier = Modifier.size(48.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = stringResource(id = R.string.quick_chat_edit),
|
||||
|
|
@ -359,7 +329,7 @@ private fun QuickChatItem(
|
|||
contentDescription = stringResource(id = R.string.quick_chat),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -367,15 +337,7 @@ private fun QuickChatItem(
|
|||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun QuickChatItemPreview() {
|
||||
AppTheme {
|
||||
QuickChatItem(
|
||||
action = QuickChatAction(
|
||||
name = "TST",
|
||||
message = "Test",
|
||||
position = 0,
|
||||
),
|
||||
)
|
||||
}
|
||||
AppTheme { QuickChatItem(action = QuickChatAction(name = "TST", message = "Test", position = 0)) }
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
|
|
@ -383,14 +345,10 @@ private fun QuickChatItemPreview() {
|
|||
private fun EditQuickChatDialogPreview() {
|
||||
AppTheme {
|
||||
EditQuickChatDialog(
|
||||
action = QuickChatAction(
|
||||
name = "TST",
|
||||
message = "Test",
|
||||
position = 0,
|
||||
),
|
||||
action = QuickChatAction(name = "TST", message = "Test", position = 0),
|
||||
onSave = {},
|
||||
onDelete = {},
|
||||
onDismiss = {}
|
||||
onDismiss = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ import androidx.compose.ui.text.TextStyle
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ import androidx.compose.ui.text.TextStyle
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.EnvironmentMetrics
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.PaxcountProtos
|
||||
import com.geeksville.mesh.Portnums.PortNum
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig.DisplayUnits
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.MeshProtos.MeshPacket
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ import androidx.compose.ui.res.pluralStringResource
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.request.ImageRequest
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.DataPacket
|
||||
import com.geeksville.mesh.model.DeviceVersion
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.BuildConfig
|
||||
import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.NodeEntity
|
||||
import com.geeksville.mesh.ui.node.components.NodeChip
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.AudioConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos.Config.BluetoothConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.CannedMessageConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ChannelProtos.ChannelSettings
|
||||
import com.geeksville.mesh.ConfigProtos.Config.LoRaConfig
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ import androidx.compose.ui.text.fromHtml
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos.Config.DeviceConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.ExternalNotificationConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ChannelProtos.ChannelSettings
|
||||
import com.geeksville.mesh.ConfigProtos.Config.LoRaConfig
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.MQTTConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import androidx.compose.ui.text.input.ImeAction
|
|||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos.Config.NetworkConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import androidx.compose.ui.platform.LocalFocusManager
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.core.location.LocationCompat
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.ConfigProtos.Config.PositionConfig
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos.Config.PowerConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.RangeTestConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.RemoteHardwareConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ import androidx.compose.ui.platform.LocalFocusManager
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos.Config.SecurityConfig
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.SerialConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.StoreForwardConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.TelemetryConfig
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.R
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ import androidx.compose.ui.tooling.preview.PreviewScreenSizes
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.AppOnlyProtos.ChannelSet
|
||||
import com.geeksville.mesh.ChannelProtos
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.AdminProtos
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.Contact
|
||||
|
|
@ -46,23 +46,14 @@ import com.geeksville.mesh.ui.common.theme.AppTheme
|
|||
import com.geeksville.mesh.ui.contact.ContactItem
|
||||
|
||||
@Composable
|
||||
fun ShareScreen(
|
||||
viewModel: UIViewModel = hiltViewModel(),
|
||||
onConfirm: (String) -> Unit
|
||||
) {
|
||||
fun ShareScreen(viewModel: UIViewModel = hiltViewModel(), onConfirm: (String) -> Unit) {
|
||||
val contactList by viewModel.contactList.collectAsStateWithLifecycle()
|
||||
|
||||
ShareScreen(
|
||||
contacts = contactList,
|
||||
onConfirm = onConfirm,
|
||||
)
|
||||
ShareScreen(contacts = contactList, onConfirm = onConfirm)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShareScreen(
|
||||
contacts: List<Contact>,
|
||||
onConfirm: (String) -> Unit
|
||||
) {
|
||||
fun ShareScreen(contacts: List<Contact>, onConfirm: (String) -> Unit) {
|
||||
var selectedContact by remember { mutableStateOf("") }
|
||||
|
||||
Column {
|
||||
|
|
@ -73,26 +64,18 @@ fun ShareScreen(
|
|||
) {
|
||||
items(contacts, key = { it.contactKey }) { contact ->
|
||||
val selected = contact.contactKey == selectedContact
|
||||
ContactItem(
|
||||
contact = contact,
|
||||
selected = selected,
|
||||
onClick = { selectedContact = contact.contactKey },
|
||||
)
|
||||
ContactItem(contact = contact, selected = selected, onClick = { selectedContact = contact.contactKey })
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
onConfirm(selectedContact)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(24.dp),
|
||||
onClick = { onConfirm(selectedContact) },
|
||||
modifier = Modifier.fillMaxWidth().padding(24.dp),
|
||||
enabled = selectedContact.isNotEmpty(),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Default.Send,
|
||||
contentDescription = stringResource(id = R.string.share)
|
||||
contentDescription = stringResource(id = R.string.share),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -103,7 +86,8 @@ fun ShareScreen(
|
|||
private fun ShareScreenPreview() {
|
||||
AppTheme {
|
||||
ShareScreen(
|
||||
contacts = listOf(
|
||||
contacts =
|
||||
listOf(
|
||||
Contact(
|
||||
contactKey = "0^all",
|
||||
shortName = stringResource(R.string.some_username),
|
||||
|
|
|
|||
106
build-logic/convention/build.gradle.kts
Normal file
106
build-logic/convention/build.gradle.kts
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
alias(libs.plugins.android.lint)
|
||||
}
|
||||
|
||||
group = "com.geeksville.mesh.buildlogic"
|
||||
|
||||
// Configure the build-logic plugins to target JDK 21
|
||||
// This matches the JDK used to build the project, and is not related to what is running on device.
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_21
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.android.gradleApiPlugin)
|
||||
compileOnly(libs.android.tools.common)
|
||||
compileOnly(libs.compose.gradlePlugin)
|
||||
compileOnly(libs.detekt.gradle)
|
||||
compileOnly(libs.firebase.crashlytics.gradlePlugin)
|
||||
compileOnly(libs.firebase.performance.gradlePlugin)
|
||||
compileOnly(libs.kotlin.gradlePlugin)
|
||||
compileOnly(libs.ksp.gradlePlugin)
|
||||
compileOnly(libs.room.gradlePlugin)
|
||||
compileOnly(libs.spotless.gradlePlugin)
|
||||
implementation(libs.truth)
|
||||
}
|
||||
|
||||
tasks {
|
||||
validatePlugins {
|
||||
enableStricterValidation = true
|
||||
failOnWarning = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("androidApplication") {
|
||||
id = libs.plugins.meshtastic.android.application.asProvider().get().pluginId
|
||||
implementationClass = "AndroidApplicationConventionPlugin"
|
||||
}
|
||||
register("androidFlavors") {
|
||||
id = libs.plugins.meshtastic.android.application.flavors.get().pluginId
|
||||
implementationClass = "AndroidApplicationFlavorsConventionPlugin"
|
||||
}
|
||||
register("androidLibrary") {
|
||||
id = libs.plugins.meshtastic.android.library.asProvider().get().pluginId
|
||||
implementationClass = "AndroidLibraryConventionPlugin"
|
||||
}
|
||||
register("androidLint") {
|
||||
id = libs.plugins.meshtastic.android.lint.get().pluginId
|
||||
implementationClass = "AndroidLintConventionPlugin"
|
||||
}
|
||||
register("androidFirebase") {
|
||||
id = libs.plugins.meshtastic.android.application.firebase.get().pluginId
|
||||
implementationClass = "AndroidApplicationFirebaseConventionPlugin"
|
||||
}
|
||||
register("androidLibraryCompose") {
|
||||
id = libs.plugins.meshtastic.android.library.compose.get().pluginId
|
||||
implementationClass = "AndroidLibraryComposeConventionPlugin"
|
||||
}
|
||||
register("androidApplicationCompose") {
|
||||
id = libs.plugins.meshtastic.android.application.compose.get().pluginId
|
||||
implementationClass = "AndroidApplicationComposeConventionPlugin"
|
||||
}
|
||||
register("hilt") {
|
||||
id = libs.plugins.meshtastic.hilt.get().pluginId
|
||||
implementationClass = "HiltConventionPlugin"
|
||||
}
|
||||
register("detekt") {
|
||||
id = libs.plugins.meshtastic.detekt.get().pluginId
|
||||
implementationClass = "DetektConventionPlugin"
|
||||
}
|
||||
register("androidRoom") {
|
||||
id = libs.plugins.meshtastic.android.room.get().pluginId
|
||||
implementationClass = "AndroidRoomConventionPlugin"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.geeksville.mesh.buildlogic.configureAndroidCompose
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
|
||||
class AndroidApplicationComposeConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "com.android.application")
|
||||
apply(plugin = "org.jetbrains.kotlin.plugin.compose")
|
||||
|
||||
val extension = extensions.getByType<ApplicationExtension>()
|
||||
configureAndroidCompose(extension)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.diffplug.gradle.spotless.SpotlessExtension
|
||||
import com.geeksville.mesh.buildlogic.configureKotlinAndroid
|
||||
import com.geeksville.mesh.buildlogic.configureSpotless
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
class AndroidApplicationConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
|
||||
apply(plugin = "com.android.application")
|
||||
apply(plugin = "org.jetbrains.kotlin.android")
|
||||
apply(plugin = "meshtastic.android.lint")
|
||||
apply(plugin = "meshtastic.android.room")
|
||||
apply(plugin = "meshtastic.hilt")
|
||||
apply(plugin = "com.diffplug.spotless")
|
||||
apply(plugin = "com.autonomousapps.dependency-analysis")
|
||||
|
||||
extensions.configure<ApplicationExtension> {
|
||||
configureKotlinAndroid(this)
|
||||
defaultConfig.targetSdk = 36
|
||||
testOptions.animationsDisabled = true
|
||||
|
||||
defaultConfig {
|
||||
targetSdk = 36
|
||||
testInstrumentationRunner = "com.geeksville.mesh.TestRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
getByName("release") {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
getByName("debug") {
|
||||
isDebuggable = true
|
||||
isPseudoLocalesEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extensions.configure<SpotlessExtension> {
|
||||
configureSpotless(this)
|
||||
}
|
||||
|
||||
extensions.configure<JavaPluginExtension> {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
compilerOptions {
|
||||
freeCompilerArgs.addAll(
|
||||
"-opt-in=kotlin.RequiresOptIn",
|
||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-Xcontext-receivers",
|
||||
"-Xannotation-default-target=param-property",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.geeksville.mesh.buildlogic.libs
|
||||
import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.exclude
|
||||
|
||||
class AndroidApplicationFirebaseConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "com.google.gms.google-services")
|
||||
apply(plugin = "com.google.firebase.firebase-perf")
|
||||
apply(plugin = "com.google.firebase.crashlytics")
|
||||
|
||||
dependencies {
|
||||
val bom = libs.findLibrary("firebase-bom").get()
|
||||
"implementation"(platform(bom))
|
||||
"implementation"(libs.findBundle("firebase").get()){
|
||||
/*
|
||||
Exclusion of protobuf / protolite dependencies is necessary as the
|
||||
datastore-proto brings in protobuf dependencies. These are the source of truth
|
||||
for Now in Android.
|
||||
That's why the duplicate classes from below dependencies are excluded.
|
||||
*/
|
||||
exclude(group = "com.google.protobuf", module = "protobuf-java")
|
||||
exclude(group = "com.google.protobuf", module = "protobuf-kotlin")
|
||||
exclude(group = "com.google.protobuf", module = "protobuf-javalite")
|
||||
exclude(group = "com.google.firebase", module = "protolite-well-known-types")
|
||||
}
|
||||
"implementation"(libs.findLibrary("firebase.crashlytics").get())
|
||||
}
|
||||
|
||||
extensions.configure<ApplicationExtension> {
|
||||
buildTypes.configureEach {
|
||||
// Disable the Crashlytics mapping file upload. This feature should only be
|
||||
// enabled if a Firebase backend is available and configured in
|
||||
// google-services.json.
|
||||
configure<CrashlyticsExtension> {
|
||||
mappingFileUploadEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.geeksville.mesh.buildlogic.configureFlavors
|
||||
import com.geeksville.mesh.buildlogic.libs
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.exclude
|
||||
|
||||
class AndroidApplicationFlavorsConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
extensions.configure<ApplicationExtension> {
|
||||
configureFlavors(this)
|
||||
dependencies {
|
||||
// F-Droid specific dependencies
|
||||
"fdroidImplementation"(libs.findBundle("osm").get())
|
||||
"fdroidImplementation"(libs.findLibrary("osmdroid-geopackage").get()) {
|
||||
exclude(group = "com.j256.ormlite")
|
||||
}
|
||||
|
||||
// Google specific dependencies
|
||||
"googleImplementation"(libs.findBundle("maps-compose").get())
|
||||
"googleImplementation"(libs.findLibrary("awesome-app-rating").get())
|
||||
"googleImplementation"(platform(libs.findLibrary("firebase-bom").get()))
|
||||
"googleImplementation"(libs.findBundle("datadog").get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import com.android.build.api.dsl.LibraryExtension
|
||||
import com.geeksville.mesh.buildlogic.configureAndroidCompose
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
|
||||
class AndroidLibraryComposeConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "com.android.library")
|
||||
apply(plugin = "org.jetbrains.kotlin.plugin.compose")
|
||||
apply(plugin = "com.autonomousapps.dependency-analysis")
|
||||
|
||||
val extension = extensions.getByType<LibraryExtension>()
|
||||
configureAndroidCompose(extension)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import com.android.build.api.dsl.LibraryExtension
|
||||
import com.android.build.api.variant.LibraryAndroidComponentsExtension
|
||||
import com.diffplug.gradle.spotless.SpotlessExtension
|
||||
import com.geeksville.mesh.buildlogic.configureFlavors
|
||||
import com.geeksville.mesh.buildlogic.configureKotlinAndroid
|
||||
import com.geeksville.mesh.buildlogic.configureSpotless
|
||||
import com.geeksville.mesh.buildlogic.disableUnnecessaryAndroidTests
|
||||
import com.geeksville.mesh.buildlogic.libs
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
class AndroidLibraryConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "com.android.library")
|
||||
apply(plugin = "org.jetbrains.kotlin.android")
|
||||
apply(plugin = "meshtastic.android.lint")
|
||||
apply(plugin = "com.diffplug.spotless")
|
||||
|
||||
extensions.configure<LibraryExtension> {
|
||||
configureKotlinAndroid(this)
|
||||
testOptions.targetSdk = 36
|
||||
lint.targetSdk = 36
|
||||
defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
testOptions.animationsDisabled = true
|
||||
configureFlavors(this)
|
||||
// The resource prefix is derived from the module name,
|
||||
// so resources inside ":core:module1" must be prefixed with "core_module1_"
|
||||
resourcePrefix =
|
||||
path.split("""\W""".toRegex()).drop(1).distinct().joinToString(separator = "_")
|
||||
.lowercase() + "_"
|
||||
}
|
||||
extensions.configure<LibraryAndroidComponentsExtension> {
|
||||
disableUnnecessaryAndroidTests(target)
|
||||
}
|
||||
dependencies {
|
||||
"androidTestImplementation"(libs.findLibrary("kotlin.test").get())
|
||||
"testImplementation"(libs.findLibrary("kotlin.test").get())
|
||||
|
||||
"implementation"(libs.findLibrary("androidx.tracing.ktx").get())
|
||||
}
|
||||
extensions.configure<SpotlessExtension> {
|
||||
configureSpotless(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.android.build.api.dsl.LibraryExtension
|
||||
import com.android.build.api.dsl.Lint
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
class AndroidLintConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
when {
|
||||
pluginManager.hasPlugin("com.android.application") ->
|
||||
configure<ApplicationExtension> { lint(Lint::configure) }
|
||||
|
||||
pluginManager.hasPlugin("com.android.library") ->
|
||||
configure<LibraryExtension> { lint(Lint::configure) }
|
||||
|
||||
else -> {
|
||||
apply(plugin = "com.android.lint")
|
||||
configure<Lint>(Lint::configure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Lint.configure() {
|
||||
xmlReport = true
|
||||
sarifReport = true
|
||||
checkDependencies = true
|
||||
abortOnError = false
|
||||
disable += "GradleDependency"
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import androidx.room.gradle.RoomExtension
|
||||
import com.geeksville.mesh.buildlogic.libs
|
||||
import com.google.devtools.ksp.gradle.KspExtension
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
class AndroidRoomConventionPlugin : Plugin<Project> {
|
||||
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "androidx.room")
|
||||
apply(plugin = "com.google.devtools.ksp")
|
||||
|
||||
extensions.configure<KspExtension> {
|
||||
arg("room.generateKotlin", "true")
|
||||
}
|
||||
|
||||
extensions.configure<RoomExtension> {
|
||||
// The schemas directory contains a schema file for each version of the Room database.
|
||||
// This is required to enable Room auto migrations.
|
||||
// See https://developer.android.com/reference/kotlin/androidx/room/AutoMigration.
|
||||
schemaDirectory("$projectDir/schemas")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"implementation"(libs.findLibrary("room.runtime").get())
|
||||
"implementation"(libs.findLibrary("room.ktx").get())
|
||||
"ksp"(libs.findLibrary("room.compiler").get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import com.geeksville.mesh.buildlogic.configureDetekt
|
||||
import com.geeksville.mesh.buildlogic.libs
|
||||
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
|
||||
class DetektConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = libs.findPlugin("detekt").get().get().pluginId)
|
||||
val extension = extensions.getByType<DetektExtension>()
|
||||
configureDetekt(extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import com.geeksville.mesh.buildlogic.libs
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
class HiltConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
with(target) {
|
||||
apply(plugin = "com.google.devtools.ksp")
|
||||
|
||||
dependencies {
|
||||
"ksp"(libs.findLibrary("hilt.compiler").get())
|
||||
"implementation"(libs.findBundle("hilt").get())
|
||||
}
|
||||
|
||||
// Add support for Jvm Module, base on org.jetbrains.kotlin.jvm
|
||||
pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
|
||||
dependencies {
|
||||
"implementation"(libs.findLibrary("hilt.core").get())
|
||||
}
|
||||
}
|
||||
|
||||
pluginManager.withPlugin("com.android.base") {
|
||||
apply(plugin = "dagger.hilt.android.plugin")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.buildlogic
|
||||
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension
|
||||
import kotlin.apply
|
||||
import kotlin.io.toRelativeString
|
||||
import kotlin.let
|
||||
import kotlin.takeIf
|
||||
import kotlin.text.toBoolean
|
||||
|
||||
/**
|
||||
* Configure Compose-specific options
|
||||
*/
|
||||
internal fun Project.configureAndroidCompose(
|
||||
commonExtension: CommonExtension<*, *, *, *, *, *>,
|
||||
) {
|
||||
commonExtension.apply {
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val bom = libs.findLibrary("androidx-compose-bom").get()
|
||||
"implementation"(platform(bom))
|
||||
"androidTestImplementation"(platform(bom))
|
||||
"implementation"(libs.findBundle("ui").get())
|
||||
"implementation"(libs.findBundle("adaptive").get())
|
||||
"implementation"(libs.findBundle("lifecycle").get())
|
||||
"implementation"(libs.findBundle("navigation").get())
|
||||
"implementation"(libs.findBundle("navigation3").get())
|
||||
"implementation"(libs.findBundle("ui-tooling").get())
|
||||
"implementation"(libs.findLibrary("androidx-compose-ui-tooling-preview").get())
|
||||
"debugImplementation"(libs.findLibrary("androidx-compose-ui-tooling").get())
|
||||
"debugImplementation"(libs.findLibrary("androidx-compose-ui-testManifest").get())
|
||||
}
|
||||
}
|
||||
|
||||
extensions.configure<ComposeCompilerGradlePluginExtension> {
|
||||
fun Provider<String>.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } }
|
||||
fun Provider<*>.relativeToRootProject(dir: String) = map {
|
||||
isolated.rootProject.projectDirectory
|
||||
.dir("build")
|
||||
.dir(projectDir.toRelativeString(rootDir))
|
||||
}.map { it.dir(dir) }
|
||||
|
||||
project.providers.gradleProperty("enableComposeCompilerMetrics").onlyIfTrue()
|
||||
.relativeToRootProject("compose-metrics")
|
||||
.let(metricsDestination::set)
|
||||
|
||||
project.providers.gradleProperty("enableComposeCompilerReports").onlyIfTrue()
|
||||
.relativeToRootProject("compose-reports")
|
||||
.let(reportsDestination::set)
|
||||
|
||||
stabilityConfigurationFiles
|
||||
.add(isolated.rootProject.projectDirectory.file("compose_compiler_config.conf"))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.buildlogic
|
||||
|
||||
import com.android.build.api.variant.LibraryAndroidComponentsExtension
|
||||
import org.gradle.api.Project
|
||||
import kotlin.io.resolve
|
||||
|
||||
/**
|
||||
* Disable unnecessary Android instrumented tests for the [project] if there is no `androidTest` folder.
|
||||
* Otherwise, these projects would be compiled, packaged, installed and ran only to end-up with the following message:
|
||||
*
|
||||
* > Starting 0 tests on AVD
|
||||
*
|
||||
* Note: this could be improved by checking other potential sourceSets based on buildTypes and flavors.
|
||||
*/
|
||||
internal fun LibraryAndroidComponentsExtension.disableUnnecessaryAndroidTests(
|
||||
project: Project,
|
||||
) = beforeVariants {
|
||||
it.androidTest.enable = it.androidTest.enable
|
||||
&& project.projectDir.resolve("src/androidTest").exists()
|
||||
}
|
||||
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.buildlogic
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.buildlogic
|
||||
|
||||
import io.gitlab.arturbosch.detekt.Detekt
|
||||
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.named
|
||||
|
||||
internal fun Project.configureDetekt(extension: DetektExtension) = extension.apply {
|
||||
extension.apply {
|
||||
toolVersion = libs.findVersion("detekt").get().toString()
|
||||
config.setFrom("$rootDir/config/detekt/detekt.yml")
|
||||
buildUponDefaultConfig = true
|
||||
allRules = false
|
||||
baseline = file("$rootDir/config/detekt/baseline.xml")
|
||||
source.setFrom(
|
||||
files(
|
||||
"src/main/java",
|
||||
"src/main/kotlin",
|
||||
),
|
||||
)
|
||||
}
|
||||
tasks.named<Detekt>("detekt") {
|
||||
reports {
|
||||
xml.required.set(true)
|
||||
html.required.set(true)
|
||||
txt.required.set(true)
|
||||
sarif.required.set(true)
|
||||
md.required.set(true)
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
"detektPlugins"(libs.findLibrary("detekt-formatting").get())
|
||||
"detektPlugins"(libs.findLibrary("detekt-compose").get())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.buildlogic
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
|
|
@ -34,7 +53,8 @@ abstract class GitVersionValueSource : ValueSource<String, GitVersionValueSource
|
|||
}
|
||||
output.toString().trim()
|
||||
} catch (e: Exception) {
|
||||
// Fallback to timestamp if git command fails (e.g., in a shallow clone)
|
||||
(System.currentTimeMillis() / 1000).toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.buildlogic
|
||||
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.kotlin.dsl.assign
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
|
||||
import kotlin.apply
|
||||
import kotlin.text.toBoolean
|
||||
|
||||
/**
|
||||
* Configure base Kotlin with Android options
|
||||
*/
|
||||
internal fun Project.configureKotlinAndroid(
|
||||
commonExtension: CommonExtension<*, *, *, *, *, *>,
|
||||
) {
|
||||
commonExtension.apply {
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
configureKotlin<KotlinAndroidProjectExtension>()
|
||||
|
||||
dependencies {
|
||||
"coreLibraryDesugaring"(libs.findLibrary("android.desugarJdkLibs").get())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure base Kotlin options for JVM (non-Android)
|
||||
*/
|
||||
internal fun Project.configureKotlinJvm() {
|
||||
extensions.configure<JavaPluginExtension> {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
configureKotlin<KotlinJvmProjectExtension>()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure base Kotlin options
|
||||
*/
|
||||
private inline fun <reified T : KotlinBaseExtension> Project.configureKotlin() = configure<T> {
|
||||
// Treat all Kotlin warnings as errors (disabled by default)
|
||||
// Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
|
||||
val warningsAsErrors = providers.gradleProperty("warningsAsErrors").map {
|
||||
it.toBoolean()
|
||||
}.orElse(false)
|
||||
when (this) {
|
||||
is KotlinAndroidProjectExtension -> compilerOptions
|
||||
is KotlinJvmProjectExtension -> compilerOptions
|
||||
else -> TODO("Unsupported project extension $this ${T::class}")
|
||||
}.apply {
|
||||
jvmTarget.assign(JvmTarget.JVM_21)
|
||||
allWarningsAsErrors.assign(warningsAsErrors)
|
||||
freeCompilerArgs.add(
|
||||
// Enable experimental coroutines APIs, including Flow
|
||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
)
|
||||
freeCompilerArgs.add(
|
||||
/**
|
||||
* Remove this args after Phase 3.
|
||||
* https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-consistent-copy-visibility/#deprecation-timeline
|
||||
*
|
||||
* Deprecation timeline
|
||||
* Phase 3. (Supposedly Kotlin 2.2 or Kotlin 2.3).
|
||||
* The default changes.
|
||||
* Unless ExposedCopyVisibility is used, the generated 'copy' method has the same visibility as the primary constructor.
|
||||
* The binary signature changes. The error on the declaration is no longer reported.
|
||||
* '-Xconsistent-data-class-copy-visibility' compiler flag and ConsistentCopyVisibility annotation are now unnecessary.
|
||||
*/
|
||||
"-Xconsistent-data-class-copy-visibility"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.buildlogic
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.android.build.api.dsl.ApplicationProductFlavor
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import com.android.build.api.dsl.ProductFlavor
|
||||
|
||||
@Suppress("EnumEntryName")
|
||||
enum class FlavorDimension {
|
||||
marketplace
|
||||
}
|
||||
|
||||
@Suppress("EnumEntryName")
|
||||
enum class MeshtasticFlavor(val dimension: FlavorDimension, val default: Boolean = false) {
|
||||
fdroid(FlavorDimension.marketplace, ),
|
||||
google(FlavorDimension.marketplace, default = true),
|
||||
}
|
||||
|
||||
fun configureFlavors(
|
||||
commonExtension: CommonExtension<*, *, *, *, *, *>,
|
||||
flavorConfigurationBlock: ProductFlavor.(flavor: MeshtasticFlavor) -> Unit = {},
|
||||
) {
|
||||
commonExtension.apply {
|
||||
FlavorDimension.entries.forEach { flavorDimension ->
|
||||
flavorDimensions += flavorDimension.name
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
MeshtasticFlavor.entries.forEach { meshtasticFlavor ->
|
||||
register(meshtasticFlavor.name) {
|
||||
dimension = meshtasticFlavor.dimension.name
|
||||
flavorConfigurationBlock(this, meshtasticFlavor)
|
||||
if (this@apply is ApplicationExtension && this is ApplicationProductFlavor) {
|
||||
if (meshtasticFlavor.default) {
|
||||
isDefault = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.buildlogic
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.VersionCatalog
|
||||
import org.gradle.api.artifacts.VersionCatalogsExtension
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
|
||||
val Project.libs
|
||||
get(): VersionCatalog = extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.buildlogic
|
||||
|
||||
import com.diffplug.gradle.spotless.SpotlessExtension
|
||||
import io.gitlab.arturbosch.detekt.Detekt
|
||||
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.named
|
||||
|
||||
internal fun Project.configureSpotless(extension: SpotlessExtension) = extension.apply {
|
||||
extension.apply {
|
||||
ratchetFrom("origin/main")
|
||||
kotlin {
|
||||
target("src/*/kotlin/**/*.kt", "src/*/java/**/*.kt")
|
||||
targetExclude("**/build/**/*.kt")
|
||||
ktfmt().kotlinlangStyle().configure { it.setMaxWidth(120) }
|
||||
ktlint("1.7.1").setEditorConfigPath("../config/spotless/.editorconfig")
|
||||
licenseHeaderFile(rootProject.file("config/spotless/copyright.kt"))
|
||||
}
|
||||
kotlinGradle {
|
||||
target("**/*.gradle.kts")
|
||||
ktfmt().kotlinlangStyle().configure { it.setMaxWidth(120) }
|
||||
ktlint("1.7.1").setEditorConfigPath("../config/spotless/.editorconfig")
|
||||
licenseHeaderFile(
|
||||
rootProject.file("config/spotless/copyright.kts"),
|
||||
"(^(?![\\/ ]\\*).*$)"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
6
build-logic/gradle.properties
Normal file
6
build-logic/gradle.properties
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Gradle properties are not passed to included builds https://github.com/gradle/gradle/issues/2534
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.configuration-cache=true
|
||||
org.gradle.configuration-cache.parallel=true
|
||||
47
build-logic/settings.gradle.kts
Normal file
47
build-logic/settings.gradle.kts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("com\\.android.*")
|
||||
includeGroupByRegex("com\\.google.*")
|
||||
includeGroupByRegex("androidx.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
}
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "build-logic"
|
||||
include(":convention")
|
||||
|
|
@ -15,6 +15,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.android.library) apply false
|
||||
|
|
@ -22,18 +24,25 @@ plugins {
|
|||
alias(libs.plugins.datadog) apply false
|
||||
alias(libs.plugins.devtools.ksp) apply false
|
||||
alias(libs.plugins.firebase.crashlytics) apply false
|
||||
alias(libs.plugins.firebase.perf) apply false
|
||||
alias(libs.plugins.google.services) apply false
|
||||
alias(libs.plugins.hilt) apply false
|
||||
alias(libs.plugins.room) apply false
|
||||
alias(libs.plugins.kotlin.android) apply false
|
||||
alias(libs.plugins.kotlin.jvm) apply false
|
||||
alias(libs.plugins.kotlin.parcelize) apply false
|
||||
alias(libs.plugins.kotlin.serialization) apply false
|
||||
alias(libs.plugins.protobuf) apply false
|
||||
alias(libs.plugins.secrets) apply false
|
||||
alias(libs.plugins.dokka) apply false
|
||||
alias(libs.plugins.dependency.analysis) apply false
|
||||
alias(libs.plugins.detekt) apply false
|
||||
alias(libs.plugins.meshtastic.detekt) apply false
|
||||
alias(libs.plugins.kover)
|
||||
alias(libs.plugins.spotless) apply false
|
||||
}
|
||||
|
||||
|
||||
|
||||
kover {
|
||||
reports {
|
||||
total {
|
||||
|
|
@ -69,8 +78,4 @@ dependencies {
|
|||
kover(project(":app"))
|
||||
kover(project(":network"))
|
||||
kover(project(":mesh_service_example"))
|
||||
}
|
||||
|
||||
tasks.register<Delete>("clean") {
|
||||
delete(layout.buildDirectory)
|
||||
}
|
||||
}
|
||||
621
config/detekt/baseline.xml
Normal file
621
config/detekt/baseline.xml
Normal file
|
|
@ -0,0 +1,621 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>ChainWrapping:Channel.kt$Channel$&&</ID>
|
||||
<ID>CommentSpacing:BLEException.kt$BLEConnectionClosing$/// Our interface is being shut down</ID>
|
||||
<ID>CommentSpacing:Constants.kt$/// a bool true means we expect this condition to continue until, false means device might come back</ID>
|
||||
<ID>CommentSpacing:ContextExtensions.kt$/// Utility function to hide the soft keyboard per stack overflow</ID>
|
||||
<ID>CommentSpacing:ContextExtensions.kt$/// show a toast</ID>
|
||||
<ID>CommentSpacing:Coroutines.kt$/// Wrap launch with an exception handler, FIXME, move into a utility lib</ID>
|
||||
<ID>CommentSpacing:DeferredExecution.kt$DeferredExecution$/// Queue some new work</ID>
|
||||
<ID>CommentSpacing:DeferredExecution.kt$DeferredExecution$/// run all work in the queue and clear it to be ready to accept new work</ID>
|
||||
<ID>CommentSpacing:Exceptions.kt$/// Convert any exceptions in this service call into a RemoteException that the client can</ID>
|
||||
<ID>CommentSpacing:Exceptions.kt$/// then handle</ID>
|
||||
<ID>CommentSpacing:Exceptions.kt$Exceptions$/// Set in Application.onCreate</ID>
|
||||
<ID>CommentWrapping:SignalMetrics.kt$Metric.SNR$/* Selected 12 as the max to get 4 equal vertical sections. */</ID>
|
||||
<ID>ComposableNaming:NodeDetail.kt$notesSection</ID>
|
||||
<ID>ComposableParamOrder:AlertDialogs.kt$SimpleAlertDialog</ID>
|
||||
<ID>ComposableParamOrder:BatteryInfo.kt$BatteryInfo</ID>
|
||||
<ID>ComposableParamOrder:ChannelSettingsItemList.kt$ChannelSettingsItemList</ID>
|
||||
<ID>ComposableParamOrder:Connections.kt$ConnectionsScreen</ID>
|
||||
<ID>ComposableParamOrder:CurrentlyConnectedCard.kt$CurrentlyConnectedCard</ID>
|
||||
<ID>ComposableParamOrder:Debug.kt$DebugMenuActions</ID>
|
||||
<ID>ComposableParamOrder:Debug.kt$DecodedPayloadBlock</ID>
|
||||
<ID>ComposableParamOrder:DebugSearch.kt$DebugSearchState</ID>
|
||||
<ID>ComposableParamOrder:DebugSearch.kt$DebugSearchStateviewModelDefaults</ID>
|
||||
<ID>ComposableParamOrder:DeviceMetrics.kt$DeviceMetricsChart</ID>
|
||||
<ID>ComposableParamOrder:EditBase64Preference.kt$EditBase64Preference</ID>
|
||||
<ID>ComposableParamOrder:EditTextPreference.kt$EditTextPreference</ID>
|
||||
<ID>ComposableParamOrder:ElevationInfo.kt$ElevationInfo</ID>
|
||||
<ID>ComposableParamOrder:EmptyStateContent.kt$EmptyStateContent</ID>
|
||||
<ID>ComposableParamOrder:EnvironmentCharts.kt$ChartContent</ID>
|
||||
<ID>ComposableParamOrder:EnvironmentCharts.kt$EnvironmentMetricsChart</ID>
|
||||
<ID>ComposableParamOrder:EnvironmentCharts.kt$MetricPlottingCanvas</ID>
|
||||
<ID>ComposableParamOrder:HostMetricsLog.kt$HostMetricsItem</ID>
|
||||
<ID>ComposableParamOrder:HostMetricsLog.kt$LogLine</ID>
|
||||
<ID>ComposableParamOrder:LastHeardInfo.kt$LastHeardInfo</ID>
|
||||
<ID>ComposableParamOrder:LinkedCoordinates.kt$LinkedCoordinates</ID>
|
||||
<ID>ComposableParamOrder:MainAppBar.kt$MainAppBar</ID>
|
||||
<ID>ComposableParamOrder:MapReportingPreference.kt$MapReportingPreference</ID>
|
||||
<ID>ComposableParamOrder:MaterialBatteryInfo.kt$MaterialBatteryInfo</ID>
|
||||
<ID>ComposableParamOrder:Message.kt$MessageScreen</ID>
|
||||
<ID>ComposableParamOrder:Message.kt$QuickChatRow</ID>
|
||||
<ID>ComposableParamOrder:MessageActions.kt$MessageActions</ID>
|
||||
<ID>ComposableParamOrder:MessageActions.kt$MessageStatusButton</ID>
|
||||
<ID>ComposableParamOrder:MessageItem.kt$MessageItem</ID>
|
||||
<ID>ComposableParamOrder:MessageList.kt$DeliveryInfo</ID>
|
||||
<ID>ComposableParamOrder:MessageList.kt$MessageList</ID>
|
||||
<ID>ComposableParamOrder:NodeChip.kt$NodeChip</ID>
|
||||
<ID>ComposableParamOrder:NodeDetail.kt$DeviceActions</ID>
|
||||
<ID>ComposableParamOrder:NodeDetail.kt$EnvironmentMetrics</ID>
|
||||
<ID>ComposableParamOrder:NodeDetail.kt$NodeActionButton</ID>
|
||||
<ID>ComposableParamOrder:NodeDetail.kt$NodeDetailList</ID>
|
||||
<ID>ComposableParamOrder:NodeDetail.kt$NodeDetailScreen</ID>
|
||||
<ID>ComposableParamOrder:NodeFilterTextField.kt$NodeFilterTextField</ID>
|
||||
<ID>ComposableParamOrder:NodeItem.kt$NodeItem</ID>
|
||||
<ID>ComposableParamOrder:NodeKeyStatusIcon.kt$NodeKeyStatusIcon</ID>
|
||||
<ID>ComposableParamOrder:NodeMenu.kt$NodeMenu</ID>
|
||||
<ID>ComposableParamOrder:NodeScreen.kt$NodeScreen</ID>
|
||||
<ID>ComposableParamOrder:PaxMetrics.kt$PaxMetricsChart</ID>
|
||||
<ID>ComposableParamOrder:PermissionScreenLayout.kt$PermissionScreenLayout</ID>
|
||||
<ID>ComposableParamOrder:PositionConfigItemList.kt$PositionConfigItemList</ID>
|
||||
<ID>ComposableParamOrder:PowerMetrics.kt$PowerMetricsChart</ID>
|
||||
<ID>ComposableParamOrder:QuickChat.kt$OutlinedTextFieldWithCounter</ID>
|
||||
<ID>ComposableParamOrder:SatelliteCountInfo.kt$SatelliteCountInfo</ID>
|
||||
<ID>ComposableParamOrder:SecurityConfigItemList.kt$SecurityConfigItemList</ID>
|
||||
<ID>ComposableParamOrder:SettingsItem.kt$SettingsItem</ID>
|
||||
<ID>ComposableParamOrder:SignalInfo.kt$SignalInfo</ID>
|
||||
<ID>ComposableParamOrder:SignalMetrics.kt$SignalMetricsChart</ID>
|
||||
<ID>ComposableParamOrder:SwitchPreference.kt$SwitchPreference</ID>
|
||||
<ID>ComposableParamOrder:TopLevelNavIcon.kt$ConnectionsNavIcon</ID>
|
||||
<ID>ComposableParamOrder:TracerouteButton.kt$TracerouteButton</ID>
|
||||
<ID>ComposableParamOrder:WarningDialog.kt$WarningDialog</ID>
|
||||
<ID>ConstructorParameterNaming:MeshLog.kt$MeshLog$@ColumnInfo(name = "message") val raw_message: String</ID>
|
||||
<ID>ConstructorParameterNaming:MeshLog.kt$MeshLog$@ColumnInfo(name = "received_date") val received_date: Long</ID>
|
||||
<ID>ConstructorParameterNaming:MeshLog.kt$MeshLog$@ColumnInfo(name = "type") val message_type: String</ID>
|
||||
<ID>ConstructorParameterNaming:Packet.kt$ContactSettings$@PrimaryKey val contact_key: String</ID>
|
||||
<ID>ConstructorParameterNaming:Packet.kt$Packet$@ColumnInfo(name = "contact_key") val contact_key: String</ID>
|
||||
<ID>ConstructorParameterNaming:Packet.kt$Packet$@ColumnInfo(name = "port_num") val port_num: Int</ID>
|
||||
<ID>ConstructorParameterNaming:Packet.kt$Packet$@ColumnInfo(name = "received_time") val received_time: Long</ID>
|
||||
<ID>ContentSlotReused:AdaptiveTwoPane.kt$second</ID>
|
||||
<ID>CyclomaticComplexMethod:MeshService.kt$MeshService$private fun handleReceivedData(packet: MeshPacket)</ID>
|
||||
<ID>CyclomaticComplexMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)</ID>
|
||||
<ID>EmptyCatchBlock:MeshLog.kt$MeshLog${ }</ID>
|
||||
<ID>EmptyClassBlock:DebugLogFile.kt$BinaryLogFile${ }</ID>
|
||||
<ID>EmptyFunctionBlock:NopInterface.kt$NopInterface${ }</ID>
|
||||
<ID>EmptyFunctionBlock:NsdManager.kt$<no name provided>${ }</ID>
|
||||
<ID>EmptyFunctionBlock:TrustAllX509TrustManager.kt$TrustAllX509TrustManager${}</ID>
|
||||
<ID>FinalNewline:BLEException.kt$com.geeksville.mesh.service.BLEException.kt</ID>
|
||||
<ID>FinalNewline:BluetoothInterfaceFactory.kt$com.geeksville.mesh.repository.radio.BluetoothInterfaceFactory.kt</ID>
|
||||
<ID>FinalNewline:BluetoothRepositoryModule.kt$com.geeksville.mesh.repository.bluetooth.BluetoothRepositoryModule.kt</ID>
|
||||
<ID>FinalNewline:BootCompleteReceiver.kt$com.geeksville.mesh.service.BootCompleteReceiver.kt</ID>
|
||||
<ID>FinalNewline:CoroutineDispatchers.kt$com.geeksville.mesh.CoroutineDispatchers.kt</ID>
|
||||
<ID>FinalNewline:Coroutines.kt$com.geeksville.mesh.concurrent.Coroutines.kt</ID>
|
||||
<ID>FinalNewline:DateUtils.kt$com.geeksville.mesh.android.DateUtils.kt</ID>
|
||||
<ID>FinalNewline:DebugLogFile.kt$com.geeksville.mesh.android.DebugLogFile.kt</ID>
|
||||
<ID>FinalNewline:DeferredExecution.kt$com.geeksville.mesh.concurrent.DeferredExecution.kt</ID>
|
||||
<ID>FinalNewline:DeviceVersion.kt$com.geeksville.mesh.model.DeviceVersion.kt</ID>
|
||||
<ID>FinalNewline:InterfaceId.kt$com.geeksville.mesh.repository.radio.InterfaceId.kt</ID>
|
||||
<ID>FinalNewline:InterfaceSpec.kt$com.geeksville.mesh.repository.radio.InterfaceSpec.kt</ID>
|
||||
<ID>FinalNewline:MockInterfaceFactory.kt$com.geeksville.mesh.repository.radio.MockInterfaceFactory.kt</ID>
|
||||
<ID>FinalNewline:NopInterface.kt$com.geeksville.mesh.repository.radio.NopInterface.kt</ID>
|
||||
<ID>FinalNewline:NopInterfaceFactory.kt$com.geeksville.mesh.repository.radio.NopInterfaceFactory.kt</ID>
|
||||
<ID>FinalNewline:ProbeTableProvider.kt$com.geeksville.mesh.repository.usb.ProbeTableProvider.kt</ID>
|
||||
<ID>FinalNewline:QuickChatActionRepository.kt$com.geeksville.mesh.database.QuickChatActionRepository.kt</ID>
|
||||
<ID>FinalNewline:RadioNotConnectedException.kt$com.geeksville.mesh.service.RadioNotConnectedException.kt</ID>
|
||||
<ID>FinalNewline:RegularPreference.kt$com.geeksville.mesh.ui.common.components.RegularPreference.kt</ID>
|
||||
<ID>FinalNewline:SerialConnection.kt$com.geeksville.mesh.repository.usb.SerialConnection.kt</ID>
|
||||
<ID>FinalNewline:SerialConnectionListener.kt$com.geeksville.mesh.repository.usb.SerialConnectionListener.kt</ID>
|
||||
<ID>FinalNewline:SerialInterface.kt$com.geeksville.mesh.repository.radio.SerialInterface.kt</ID>
|
||||
<ID>FinalNewline:SerialInterfaceFactory.kt$com.geeksville.mesh.repository.radio.SerialInterfaceFactory.kt</ID>
|
||||
<ID>FinalNewline:TCPInterfaceFactory.kt$com.geeksville.mesh.repository.radio.TCPInterfaceFactory.kt</ID>
|
||||
<ID>FinalNewline:UsbBroadcastReceiver.kt$com.geeksville.mesh.repository.usb.UsbBroadcastReceiver.kt</ID>
|
||||
<ID>FinalNewline:UsbRepositoryModule.kt$com.geeksville.mesh.repository.usb.UsbRepositoryModule.kt</ID>
|
||||
<ID>ForbiddenComment:SafeBluetooth.kt$SafeBluetooth$// TODO: display some kind of UI about restarting BLE</ID>
|
||||
<ID>FunctionNaming:PacketDao.kt$PacketDao$@Query("DELETE FROM packet WHERE uuid=:uuid") suspend fun _delete(uuid: Long)</ID>
|
||||
<ID>FunctionNaming:QuickChatActionDao.kt$QuickChatActionDao$@Query("Delete from quick_chat where uuid=:uuid") fun _delete(uuid: Long)</ID>
|
||||
<ID>ImplicitDefaultLocale:NodeInfo.kt$NodeInfo$String.format("%d%%", batteryLevel)</ID>
|
||||
<ID>LambdaParameterEventTrailing:Channel.kt$onConfirm</ID>
|
||||
<ID>LambdaParameterEventTrailing:CurrentlyConnectedCard.kt$onClickDisconnect</ID>
|
||||
<ID>LambdaParameterEventTrailing:MainAppBar.kt$onAction</ID>
|
||||
<ID>LambdaParameterEventTrailing:Message.kt$onClick</ID>
|
||||
<ID>LambdaParameterEventTrailing:Message.kt$onSendMessage</ID>
|
||||
<ID>LambdaParameterEventTrailing:MessageList.kt$onReply</ID>
|
||||
<ID>LambdaParameterEventTrailing:NodeChip.kt$onAction</ID>
|
||||
<ID>LambdaParameterEventTrailing:NodeDetail.kt$onClick</ID>
|
||||
<ID>LambdaParameterEventTrailing:NodeDetail.kt$onSaveNotes</ID>
|
||||
<ID>LambdaParameterInRestartableEffect:Channel.kt$onConfirm</ID>
|
||||
<ID>LambdaParameterInRestartableEffect:MessageList.kt$onUnreadChanged</ID>
|
||||
<ID>LargeClass:MeshService.kt$MeshService : ServiceLogging</ID>
|
||||
<ID>LongMethod:AmbientLightingConfigItemList.kt$@Composable fun AmbientLightingConfigItemList( ambientLightingConfig: ModuleConfigProtos.ModuleConfig.AmbientLightingConfig, enabled: Boolean, onSaveClicked: (ModuleConfigProtos.ModuleConfig.AmbientLightingConfig) -> Unit, )</ID>
|
||||
<ID>LongMethod:CannedMessageConfigItemList.kt$@Composable fun CannedMessageConfigItemList( messages: String, cannedMessageConfig: CannedMessageConfig, enabled: Boolean, onSaveClicked: (messages: String, config: CannedMessageConfig) -> Unit, )</ID>
|
||||
<ID>LongMethod:DropDownPreference.kt$@Composable fun <T> DropDownPreference( title: String, enabled: Boolean, items: List<Pair<T, String>>, selectedItem: T, onItemSelected: (T) -> Unit, modifier: Modifier = Modifier, summary: String? = null, )</ID>
|
||||
<ID>LongMethod:EditListPreference.kt$@Composable inline fun <reified T> EditListPreference( title: String, list: List<T>, maxCount: Int, enabled: Boolean, keyboardActions: KeyboardActions, crossinline onValuesChanged: (List<T>) -> Unit, modifier: Modifier = Modifier, )</ID>
|
||||
<ID>LongMethod:ExternalNotificationConfigItemList.kt$@Composable fun ExternalNotificationConfigItemList( ringtone: String, extNotificationConfig: ExternalNotificationConfig, enabled: Boolean, onSaveClicked: (ringtone: String, config: ExternalNotificationConfig) -> Unit, )</ID>
|
||||
<ID>LongMethod:MeshService.kt$MeshService$private fun handleReceivedData(packet: MeshPacket)</ID>
|
||||
<ID>LongMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)</ID>
|
||||
<ID>LongMethod:StoreForwardConfigItemList.kt$@Composable fun StoreForwardConfigItemList( storeForwardConfig: StoreForwardConfig, enabled: Boolean, onSaveClicked: (StoreForwardConfig) -> Unit, )</ID>
|
||||
<ID>LongMethod:TelemetryConfigItemList.kt$@Composable fun TelemetryConfigItemList( telemetryConfig: TelemetryConfig, enabled: Boolean, onSaveClicked: (TelemetryConfig) -> Unit, )</ID>
|
||||
<ID>MagicNumber:BatteryInfo.kt$100</ID>
|
||||
<ID>MagicNumber:BatteryInfo.kt$101</ID>
|
||||
<ID>MagicNumber:BatteryInfo.kt$14</ID>
|
||||
<ID>MagicNumber:BatteryInfo.kt$15</ID>
|
||||
<ID>MagicNumber:BatteryInfo.kt$34</ID>
|
||||
<ID>MagicNumber:BatteryInfo.kt$35</ID>
|
||||
<ID>MagicNumber:BatteryInfo.kt$4</ID>
|
||||
<ID>MagicNumber:BatteryInfo.kt$5</ID>
|
||||
<ID>MagicNumber:BatteryInfo.kt$79</ID>
|
||||
<ID>MagicNumber:BatteryInfo.kt$80</ID>
|
||||
<ID>MagicNumber:BluetoothInterface.kt$BluetoothInterface$1000</ID>
|
||||
<ID>MagicNumber:BluetoothInterface.kt$BluetoothInterface$500</ID>
|
||||
<ID>MagicNumber:BluetoothInterface.kt$BluetoothInterface$512</ID>
|
||||
<ID>MagicNumber:Channel.kt$0xff</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$.03125f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$.0625f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$.203125f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$.40625f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$.8125f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$1.6250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$1000f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$1600</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$200</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$3.25f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$31</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$400</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$5</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$62</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$800</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.LONG_FAST$.250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.LONG_MODERATE$.125f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.LONG_SLOW$.125f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.MEDIUM_FAST$.250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.MEDIUM_SLOW$.250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.SHORT_FAST$.250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.SHORT_SLOW$.250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.VERY_LONG_SLOW$.0625f</ID>
|
||||
<ID>MagicNumber:ChannelSet.kt$40</ID>
|
||||
<ID>MagicNumber:ChannelSet.kt$960</ID>
|
||||
<ID>MagicNumber:Contacts.kt$7</ID>
|
||||
<ID>MagicNumber:Contacts.kt$8</ID>
|
||||
<ID>MagicNumber:DataPacket.kt$DataPacket.CREATOR$16</ID>
|
||||
<ID>MagicNumber:Debug.kt$3</ID>
|
||||
<ID>MagicNumber:DeviceVersion.kt$DeviceVersion$100</ID>
|
||||
<ID>MagicNumber:DeviceVersion.kt$DeviceVersion$10000</ID>
|
||||
<ID>MagicNumber:EditChannelDialog.kt$16</ID>
|
||||
<ID>MagicNumber:EditChannelDialog.kt$32</ID>
|
||||
<ID>MagicNumber:EditIPv4Preference.kt$0xff</ID>
|
||||
<ID>MagicNumber:EditIPv4Preference.kt$16</ID>
|
||||
<ID>MagicNumber:EditIPv4Preference.kt$24</ID>
|
||||
<ID>MagicNumber:EditIPv4Preference.kt$8</ID>
|
||||
<ID>MagicNumber:EditListPreference.kt$12</ID>
|
||||
<ID>MagicNumber:EditListPreference.kt$12345</ID>
|
||||
<ID>MagicNumber:EditListPreference.kt$67890</ID>
|
||||
<ID>MagicNumber:Extensions.kt$1000</ID>
|
||||
<ID>MagicNumber:Extensions.kt$1440000</ID>
|
||||
<ID>MagicNumber:Extensions.kt$24</ID>
|
||||
<ID>MagicNumber:Extensions.kt$2880</ID>
|
||||
<ID>MagicNumber:Extensions.kt$60</ID>
|
||||
<ID>MagicNumber:LazyColumnDragAndDropDemo.kt$50</ID>
|
||||
<ID>MagicNumber:LocationRepository.kt$LocationRepository$1000L</ID>
|
||||
<ID>MagicNumber:LocationRepository.kt$LocationRepository$30</ID>
|
||||
<ID>MagicNumber:LocationRepository.kt$LocationRepository$31</ID>
|
||||
<ID>MagicNumber:LocationUtils.kt$1e-7</ID>
|
||||
<ID>MagicNumber:LocationUtils.kt$360</ID>
|
||||
<ID>MagicNumber:MQTTRepository.kt$MQTTRepository$512</ID>
|
||||
<ID>MagicNumber:MeshService.kt$MeshService$0xffffffff</ID>
|
||||
<ID>MagicNumber:MeshService.kt$MeshService$1000</ID>
|
||||
<ID>MagicNumber:MeshService.kt$MeshService$1000.0</ID>
|
||||
<ID>MagicNumber:MeshService.kt$MeshService$1000L</ID>
|
||||
<ID>MagicNumber:MeshService.kt$MeshService$16</ID>
|
||||
<ID>MagicNumber:MeshService.kt$MeshService$30</ID>
|
||||
<ID>MagicNumber:MeshService.kt$MeshService$32</ID>
|
||||
<ID>MagicNumber:MeshService.kt$MeshService$60000</ID>
|
||||
<ID>MagicNumber:MeshService.kt$MeshService$8</ID>
|
||||
<ID>MagicNumber:MetricsViewModel.kt$MetricsViewModel$1000L</ID>
|
||||
<ID>MagicNumber:MetricsViewModel.kt$MetricsViewModel$1e-5</ID>
|
||||
<ID>MagicNumber:MetricsViewModel.kt$MetricsViewModel$1e-7</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$DeviceMetrics.Companion$1000</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$EnvironmentMetrics.Companion$1000</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$0.114</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$0.299</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$0.587</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$0x0000FF</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$0x00FF00</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$0xFF0000</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$1000</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$1000.0</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$16</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$1609</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$1609.34</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$255</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$3.281</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$NodeInfo$8</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$Position$180</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$Position$90</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$Position$90.0</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$Position.Companion$1000</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$Position.Companion$1e-7</ID>
|
||||
<ID>MagicNumber:NodeInfo.kt$Position.Companion$1e7</ID>
|
||||
<ID>MagicNumber:PacketRepository.kt$PacketRepository$500</ID>
|
||||
<ID>MagicNumber:PacketResponseStateDialog.kt$100</ID>
|
||||
<ID>MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$21972</ID>
|
||||
<ID>MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$32809</ID>
|
||||
<ID>MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$6790</ID>
|
||||
<ID>MagicNumber:ProbeTableProvider.kt$ProbeTableProvider$9114</ID>
|
||||
<ID>MagicNumber:SafeBluetooth.kt$SafeBluetooth$10</ID>
|
||||
<ID>MagicNumber:SafeBluetooth.kt$SafeBluetooth$100</ID>
|
||||
<ID>MagicNumber:SafeBluetooth.kt$SafeBluetooth$1000</ID>
|
||||
<ID>MagicNumber:SafeBluetooth.kt$SafeBluetooth$2500</ID>
|
||||
<ID>MagicNumber:SafeBluetooth.kt$SafeBluetooth.<no name provided>$2500</ID>
|
||||
<ID>MagicNumber:SerialConnectionImpl.kt$SerialConnectionImpl$115200</ID>
|
||||
<ID>MagicNumber:SerialConnectionImpl.kt$SerialConnectionImpl$200</ID>
|
||||
<ID>MagicNumber:ServiceClient.kt$ServiceClient$500</ID>
|
||||
<ID>MagicNumber:StreamInterface.kt$StreamInterface$0xff</ID>
|
||||
<ID>MagicNumber:StreamInterface.kt$StreamInterface$3</ID>
|
||||
<ID>MagicNumber:StreamInterface.kt$StreamInterface$4</ID>
|
||||
<ID>MagicNumber:StreamInterface.kt$StreamInterface$8</ID>
|
||||
<ID>MagicNumber:TCPInterface.kt$TCPInterface$1000</ID>
|
||||
<ID>MagicNumber:TCPInterface.kt$TCPInterface$180</ID>
|
||||
<ID>MagicNumber:TCPInterface.kt$TCPInterface$500</ID>
|
||||
<ID>MagicNumber:UIState.kt$4</ID>
|
||||
<ID>MatchingDeclarationName:AnalyticsClient.kt$AnalyticsProvider</ID>
|
||||
<ID>MatchingDeclarationName:DistanceExtensions.kt$DistanceUnit</ID>
|
||||
<ID>MatchingDeclarationName:LocationUtils.kt$GPSFormat</ID>
|
||||
<ID>MatchingDeclarationName:MeshServiceStarter.kt$ServiceStarter : Worker</ID>
|
||||
<ID>MatchingDeclarationName:SortOption.kt$NodeSortOption</ID>
|
||||
<ID>MaxLineLength:BluetoothInterface.kt$/* Info for the esp32 device side code. See that source for the 'gold' standard docs on this interface. MeshBluetoothService UUID 6ba1b218-15a8-461f-9fa8-5dcae273eafd FIXME - notify vs indication for fromradio output. Using notify for now, not sure if that is best FIXME - in the esp32 mesh management code, occasionally mirror the current net db to flash, so that if we reboot we still have a good guess of users who are out there. FIXME - make sure this protocol is guaranteed robust and won't drop packets "According to the BLE specification the notification length can be max ATT_MTU - 3. The 3 bytes subtracted is the 3-byte header(OP-code (operation, 1 byte) and the attribute handle (2 bytes)). In BLE 4.1 the ATT_MTU is 23 bytes (20 bytes for payload), but in BLE 4.2 the ATT_MTU can be negotiated up to 247 bytes." MAXPACKET is 256? look into what the lora lib uses. FIXME Characteristics: UUID properties description 8ba2bcc2-ee02-4a55-a531-c525c5e454d5 read fromradio - contains a newly received packet destined towards the phone (up to MAXPACKET bytes? per packet). After reading the esp32 will put the next packet in this mailbox. If the FIFO is empty it will put an empty packet in this mailbox. f75c76d2-129e-4dad-a1dd-7866124401e7 write toradio - write ToRadio protobufs to this charstic to send them (up to MAXPACKET len) ed9da18c-a800-4f66-a670-aa7547e34453 read|notify|write fromnum - the current packet # in the message waiting inside fromradio, if the phone sees this notify it should read messages until it catches up with this number. The phone can write to this register to go backwards up to FIXME packets, to handle the rare case of a fromradio packet was dropped after the esp32 callback was called, but before it arrives at the phone. If the phone writes to this register the esp32 will discard older packets and put the next packet >= fromnum in fromradio. When the esp32 advances fromnum, it will delay doing the notify by 100ms, in the hopes that the notify will never actally need to be sent if the phone is already pulling from fromradio. Note: that if the phone ever sees this number decrease, it means the esp32 has rebooted. Re: queue management Not all messages are kept in the fromradio queue (filtered based on SubPacket): * only the most recent Position and User messages for a particular node are kept * all Data SubPackets are kept * No WantNodeNum / DenyNodeNum messages are kept A variable keepAllPackets, if set to true will suppress this behavior and instead keep everything for forwarding to the phone (for debugging) */</ID>
|
||||
<ID>MaxLineLength:BluetoothState.kt$BluetoothState$"BluetoothState(hasPermissions=$hasPermissions, enabled=$enabled, bondedDevices=${bondedDevices.map { it.anonymize }})"</ID>
|
||||
<ID>MaxLineLength:Channel.kt$Channel$// We have a new style 'empty' channel name. Use the same logic from the device to convert that to a human readable name</ID>
|
||||
<ID>MaxLineLength:DataPacket.kt$DataPacket$val dataType: Int</ID>
|
||||
<ID>MaxLineLength:LocationRepository.kt$LocationRepository$info("Starting location updates with $providerList intervalMs=${intervalMs}ms and minDistanceM=${minDistanceM}m")</ID>
|
||||
<ID>MaxLineLength:MQTTRepository.kt$MQTTRepository.Companion$*</ID>
|
||||
<ID>MaxLineLength:ServiceClient.kt$ServiceClient$// Some phones seem to ahve a race where if you unbind and quickly rebind bindService returns false. Try</ID>
|
||||
<ID>MaxLineLength:ServiceClient.kt$ServiceClient.<no name provided>$// If we start to close a service, it seems that there is a possibility a onServiceConnected event is the queue</ID>
|
||||
<ID>ModifierClickableOrder:Channel.kt$clickable(onClick = onClick)</ID>
|
||||
<ID>ModifierListSpacing:Packet.kt$Packet$@Entity( tableName = "packet", indices = [ Index(value = ["myNodeNum"]), Index(value = ["port_num"]), Index(value = ["contact_key"]), ] ) data</ID>
|
||||
<ID>ModifierMissing:AdaptiveTwoPane.kt$AdaptiveTwoPane</ID>
|
||||
<ID>ModifierMissing:AmbientLightingConfigItemList.kt$AmbientLightingConfigItemList</ID>
|
||||
<ID>ModifierMissing:AudioConfigItemList.kt$AudioConfigItemList</ID>
|
||||
<ID>ModifierMissing:BLEDevices.kt$BLEDevices</ID>
|
||||
<ID>ModifierMissing:BluetoothConfigItemList.kt$BluetoothConfigItemList</ID>
|
||||
<ID>ModifierMissing:CannedMessageConfigItemList.kt$CannedMessageConfigItemList</ID>
|
||||
<ID>ModifierMissing:Channel.kt$ChannelScreen</ID>
|
||||
<ID>ModifierMissing:ChannelSettingsItemList.kt$ChannelSelection</ID>
|
||||
<ID>ModifierMissing:CleanNodeDatabaseScreen.kt$CleanNodeDatabaseScreen</ID>
|
||||
<ID>ModifierMissing:CommonCharts.kt$ChartHeader</ID>
|
||||
<ID>ModifierMissing:CommonCharts.kt$Legend</ID>
|
||||
<ID>ModifierMissing:CommonCharts.kt$TimeLabels</ID>
|
||||
<ID>ModifierMissing:Connections.kt$ConnectionsScreen</ID>
|
||||
<ID>ModifierMissing:ContactSharing.kt$SharedContactDialog</ID>
|
||||
<ID>ModifierMissing:Contacts.kt$ContactListView</ID>
|
||||
<ID>ModifierMissing:Contacts.kt$ContactsScreen</ID>
|
||||
<ID>ModifierMissing:Contacts.kt$SelectionToolbar</ID>
|
||||
<ID>ModifierMissing:DetectionSensorConfigItemList.kt$DetectionSensorConfigItemList</ID>
|
||||
<ID>ModifierMissing:DeviceConfigItemList.kt$DeviceConfigItemList</ID>
|
||||
<ID>ModifierMissing:DeviceMetrics.kt$DeviceMetricsScreen</ID>
|
||||
<ID>ModifierMissing:DisplayConfigItemList.kt$DisplayConfigItemList</ID>
|
||||
<ID>ModifierMissing:EmojiPicker.kt$EmojiPicker</ID>
|
||||
<ID>ModifierMissing:EmojiPicker.kt$EmojiPickerDialog</ID>
|
||||
<ID>ModifierMissing:EmptyStateContent.kt$EmptyStateContent</ID>
|
||||
<ID>ModifierMissing:EnvironmentMetrics.kt$EnvironmentMetricsScreen</ID>
|
||||
<ID>ModifierMissing:ExternalNotificationConfigItemList.kt$ExternalNotificationConfigItemList</ID>
|
||||
<ID>ModifierMissing:HostMetricsLog.kt$HostMetricsLogScreen</ID>
|
||||
<ID>ModifierMissing:IndoorAirQuality.kt$IndoorAirQuality</ID>
|
||||
<ID>ModifierMissing:LoRaConfigItemList.kt$LoRaConfigItemList</ID>
|
||||
<ID>ModifierMissing:LoraSignalIndicator.kt$LoraSignalIndicator</ID>
|
||||
<ID>ModifierMissing:LoraSignalIndicator.kt$Rssi</ID>
|
||||
<ID>ModifierMissing:LoraSignalIndicator.kt$Snr</ID>
|
||||
<ID>ModifierMissing:LoraSignalIndicator.kt$SnrAndRssi</ID>
|
||||
<ID>ModifierMissing:MQTTConfigItemList.kt$MQTTConfigItemList</ID>
|
||||
<ID>ModifierMissing:Main.kt$MainScreen</ID>
|
||||
<ID>ModifierMissing:MapReportingPreference.kt$MapReportingPreference</ID>
|
||||
<ID>ModifierMissing:MessageActions.kt$MessageStatusButton</ID>
|
||||
<ID>ModifierMissing:MessageActions.kt$ReactionButton</ID>
|
||||
<ID>ModifierMissing:MessageActions.kt$ReplyButton</ID>
|
||||
<ID>ModifierMissing:NeighborInfoConfigItemList.kt$NeighborInfoConfigItemList</ID>
|
||||
<ID>ModifierMissing:NetworkConfigItemList.kt$NetworkConfigItemList</ID>
|
||||
<ID>ModifierMissing:NetworkDevices.kt$NetworkDevices</ID>
|
||||
<ID>ModifierMissing:NodeMenu.kt$NodeMenu</ID>
|
||||
<ID>ModifierMissing:NodeScreen.kt$NodeScreen</ID>
|
||||
<ID>ModifierMissing:NodeStatusIcons.kt$NodeStatusIcons</ID>
|
||||
<ID>ModifierMissing:PaxMetrics.kt$PaxMetricsItem</ID>
|
||||
<ID>ModifierMissing:PaxMetrics.kt$PaxMetricsScreen</ID>
|
||||
<ID>ModifierMissing:PaxcounterConfigItemList.kt$PaxcounterConfigItemList</ID>
|
||||
<ID>ModifierMissing:PositionConfigItemList.kt$PositionConfigItemList</ID>
|
||||
<ID>ModifierMissing:PositionLog.kt$PositionItem</ID>
|
||||
<ID>ModifierMissing:PositionLog.kt$PositionLogScreen</ID>
|
||||
<ID>ModifierMissing:PowerConfigItemList.kt$PowerConfigItemList</ID>
|
||||
<ID>ModifierMissing:PowerMetrics.kt$PowerMetricsScreen</ID>
|
||||
<ID>ModifierMissing:RadioConfig.kt$RadioConfigItemList</ID>
|
||||
<ID>ModifierMissing:RangeTestConfigItemList.kt$RangeTestConfigItemList</ID>
|
||||
<ID>ModifierMissing:Reaction.kt$ReactionDialog</ID>
|
||||
<ID>ModifierMissing:RemoteHardwareConfigItemList.kt$RemoteHardwareConfigItemList</ID>
|
||||
<ID>ModifierMissing:SecurityConfigItemList.kt$SecurityConfigItemList</ID>
|
||||
<ID>ModifierMissing:SecurityIcon.kt$SecurityIcon</ID>
|
||||
<ID>ModifierMissing:SerialConfigItemList.kt$SerialConfigItemList</ID>
|
||||
<ID>ModifierMissing:SettingsItem.kt$SettingsItem</ID>
|
||||
<ID>ModifierMissing:SettingsItem.kt$SettingsItemDetail</ID>
|
||||
<ID>ModifierMissing:SettingsItem.kt$SettingsItemSwitch</ID>
|
||||
<ID>ModifierMissing:SettingsScreen.kt$SettingsScreen</ID>
|
||||
<ID>ModifierMissing:Share.kt$ShareScreen</ID>
|
||||
<ID>ModifierMissing:SignalMetrics.kt$SignalMetricsScreen</ID>
|
||||
<ID>ModifierMissing:SimpleAlertDialog.kt$SimpleAlertDialog</ID>
|
||||
<ID>ModifierMissing:SlidingSelector.kt$OptionLabel</ID>
|
||||
<ID>ModifierMissing:StoreForwardConfigItemList.kt$StoreForwardConfigItemList</ID>
|
||||
<ID>ModifierMissing:TelemetryConfigItemList.kt$TelemetryConfigItemList</ID>
|
||||
<ID>ModifierMissing:TopLevelNavIcon.kt$TopLevelNavIcon</ID>
|
||||
<ID>ModifierMissing:UserConfigItemList.kt$UserConfigItemList</ID>
|
||||
<ID>ModifierNotUsedAtRoot:BitwisePreference.kt$modifier = modifier .fillMaxWidth() .wrapContentWidth(Alignment.End)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:BitwisePreference.kt$modifier = modifier.fillMaxWidth()</ID>
|
||||
<ID>ModifierNotUsedAtRoot:DeviceMetrics.kt$modifier = modifier.weight(weight = Y_AXIS_WEIGHT)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:DeviceMetrics.kt$modifier = modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:DeviceMetrics.kt$modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:DropDownPreference.kt$modifier = modifier .background( color = if (selectedItem == item.first) { MaterialTheme.colorScheme.primary.copy(alpha = 0.3f) } else { Color.Unspecified }, )</ID>
|
||||
<ID>ModifierNotUsedAtRoot:EditChannelDialog.kt$modifier = modifier.weight(1f)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:EditDeviceProfileDialog.kt$modifier = modifier.weight(1f)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:EditListPreference.kt$modifier = modifier.fillMaxWidth()</ID>
|
||||
<ID>ModifierNotUsedAtRoot:EditListPreference.kt$modifier = modifier.padding(16.dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:EnvironmentCharts.kt$modifier = modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:EnvironmentCharts.kt$modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:NodeChip.kt$modifier = modifier.width(IntrinsicSize.Min).defaultMinSize(minWidth = 72.dp).semantics { contentDescription = node.user.shortName.ifEmpty { "Node" } }</ID>
|
||||
<ID>ModifierNotUsedAtRoot:PaxMetrics.kt$modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:PowerMetrics.kt$modifier = modifier.weight(weight = Y_AXIS_WEIGHT)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:PowerMetrics.kt$modifier = modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:PowerMetrics.kt$modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:PreferenceFooter.kt$modifier = modifier .height(48.dp) .weight(1f)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:SignalInfo.kt$modifier = modifier</ID>
|
||||
<ID>ModifierNotUsedAtRoot:SignalMetrics.kt$modifier = modifier.weight(weight = Y_AXIS_WEIGHT)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:SignalMetrics.kt$modifier = modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:SignalMetrics.kt$modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:TextDividerPreference.kt$modifier = modifier .fillMaxWidth() .padding(all = 16.dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:TextDividerPreference.kt$modifier = modifier .fillMaxWidth() .wrapContentWidth(Alignment.End)</ID>
|
||||
<ID>ModifierReused:BitwisePreference.kt$Checkbox( modifier = modifier .fillMaxWidth() .wrapContentWidth(Alignment.End), checked = value and item.first != 0, onCheckedChange = { onItemSelected(value xor item.first) }, enabled = enabled, )</ID>
|
||||
<ID>ModifierReused:BitwisePreference.kt$DropdownMenuItem( onClick = { onItemSelected(value xor item.first) }, modifier = modifier.fillMaxWidth(), text = { Text( text = item.second, overflow = TextOverflow.Ellipsis, ) Checkbox( modifier = modifier .fillMaxWidth() .wrapContentWidth(Alignment.End), checked = value and item.first != 0, onCheckedChange = { onItemSelected(value xor item.first) }, enabled = enabled, ) } )</ID>
|
||||
<ID>ModifierReused:DeviceMetrics.kt$Canvas(modifier = modifier.width(dp)) { val height = size.height val width = size.width for (i in telemetries.indices) { val telemetry = telemetries[i] /* x-value time */ val xRatio = (telemetry.time - oldest.time).toFloat() / timeDiff val x = xRatio * width /* Channel Utilization */ plotPoint( drawContext = drawContext, color = Device.CH_UTIL.color, x = x, value = telemetry.deviceMetrics.channelUtilization, divisor = MAX_PERCENT_VALUE, ) /* Air Utilization Transmit */ plotPoint( drawContext = drawContext, color = Device.AIR_UTIL.color, x = x, value = telemetry.deviceMetrics.airUtilTx, divisor = MAX_PERCENT_VALUE, ) } /* Battery Line */ var index = 0 while (index < telemetries.size) { val path = Path() index = createPath( telemetries = telemetries, index = index, path = path, oldestTime = oldest.time, timeRange = timeDiff, width = width, timeThreshold = selectedTime.timeThreshold(), ) { i -> val telemetry = telemetries.getOrNull(i) ?: telemetries.last() val ratio = telemetry.deviceMetrics.batteryLevel / MAX_PERCENT_VALUE val y = height - (ratio * height) return@createPath y } drawPath( path = path, color = Device.BATTERY.color, style = Stroke(width = GraphUtil.RADIUS, cap = StrokeCap.Round), ) } }</ID>
|
||||
<ID>ModifierReused:DeviceMetrics.kt$HorizontalLinesOverlay( modifier.width(dp), lineColors = listOf(graphColor, Color.Yellow, Color.Red, graphColor, graphColor), )</ID>
|
||||
<ID>ModifierReused:DeviceMetrics.kt$TimeAxisOverlay(modifier.width(dp), oldest = oldest.time, newest = newest.time, selectedTime.lineInterval())</ID>
|
||||
<ID>ModifierReused:EditListPreference.kt$Column(modifier = modifier) { Text(modifier = modifier.padding(16.dp), text = title, style = MaterialTheme.typography.bodyMedium) listState.forEachIndexed { index, value -> val trailingIcon = @Composable { IconButton( onClick = { focusManager.clearFocus() listState.removeAt(index) onValuesChanged(listState) }, ) { Icon( imageVector = Icons.TwoTone.Close, contentDescription = stringResource(R.string.delete), modifier = Modifier.wrapContentSize(), ) } } // handle lora.ignoreIncoming: List<Int> if (value is Int) { EditTextPreference( title = "${index + 1}/$maxCount", value = value, enabled = enabled, keyboardActions = keyboardActions, onValueChanged = { listState[index] = it as T onValuesChanged(listState) }, modifier = modifier.fillMaxWidth(), trailingIcon = trailingIcon, ) } // handle security.adminKey: List<ByteString> if (value is ByteString) { EditBase64Preference( title = "${index + 1}/$maxCount", value = value, enabled = enabled, keyboardActions = keyboardActions, onValueChange = { listState[index] = it as T onValuesChanged(listState) }, modifier = modifier.fillMaxWidth(), trailingIcon = trailingIcon, ) } // handle remoteHardware.availablePins: List<RemoteHardwarePin> if (value is RemoteHardwarePin) { EditTextPreference( title = stringResource(R.string.gpio_pin), value = value.gpioPin, enabled = enabled, keyboardActions = keyboardActions, onValueChanged = { if (it in 0..255) { listState[index] = value.copy { gpioPin = it } as T onValuesChanged(listState) } }, ) EditTextPreference( title = stringResource(R.string.name), value = value.name, maxSize = 14, // name max_size:15 enabled = enabled, isError = false, keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done), keyboardActions = keyboardActions, onValueChanged = { listState[index] = value.copy { name = it } as T onValuesChanged(listState) }, trailingIcon = trailingIcon, ) DropDownPreference( title = stringResource(R.string.type), enabled = enabled, items = RemoteHardwarePinType.entries .filter { it != RemoteHardwarePinType.UNRECOGNIZED } .map { it to it.name }, selectedItem = value.type, onItemSelected = { listState[index] = value.copy { type = it } as T onValuesChanged(listState) }, ) } } OutlinedButton( modifier = Modifier.fillMaxWidth(), onClick = { // Add element based on the type T val newElement = when (T::class) { Int::class -> 0 as T ByteString::class -> ByteString.EMPTY as T RemoteHardwarePin::class -> remoteHardwarePin {} as T else -> throw IllegalArgumentException("Unsupported type: ${T::class}") } listState.add(listState.size, newElement) }, enabled = maxCount > listState.size, ) { Text(text = stringResource(R.string.add)) } }</ID>
|
||||
<ID>ModifierReused:EditListPreference.kt$EditBase64Preference( title = "${index + 1}/$maxCount", value = value, enabled = enabled, keyboardActions = keyboardActions, onValueChange = { listState[index] = it as T onValuesChanged(listState) }, modifier = modifier.fillMaxWidth(), trailingIcon = trailingIcon, )</ID>
|
||||
<ID>ModifierReused:EditListPreference.kt$EditTextPreference( title = "${index + 1}/$maxCount", value = value, enabled = enabled, keyboardActions = keyboardActions, onValueChanged = { listState[index] = it as T onValuesChanged(listState) }, modifier = modifier.fillMaxWidth(), trailingIcon = trailingIcon, )</ID>
|
||||
<ID>ModifierReused:EditListPreference.kt$Text(modifier = modifier.padding(16.dp), text = title, style = MaterialTheme.typography.bodyMedium)</ID>
|
||||
<ID>ModifierReused:EditTextPreference.kt$Box( contentAlignment = Alignment.BottomEnd, modifier = modifier.fillMaxWidth() ) { Text( text = "${value.toByteArray().size}/$maxSize", style = MaterialTheme.typography.bodySmall, color = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onBackground, modifier = Modifier.padding(end = 8.dp, bottom = 4.dp) ) }</ID>
|
||||
<ID>ModifierReused:EditTextPreference.kt$TextField( value = value, singleLine = true, modifier = modifier .fillMaxWidth() .onFocusEvent { isFocused = it.isFocused; onFocusChanged(it) }, enabled = enabled, isError = isError, onValueChange = { if (maxSize > 0) { if (it.toByteArray().size <= maxSize) { onValueChanged(it) } } else onValueChanged(it) }, label = { Text(title) }, keyboardOptions = keyboardOptions, keyboardActions = keyboardActions, visualTransformation = visualTransformation, trailingIcon = { if (trailingIcon != null) { trailingIcon() } else if (isError) { Icon( imageVector = Icons.TwoTone.Info, contentDescription = stringResource(id = R.string.error), tint = MaterialTheme.colorScheme.error ) } }, )</ID>
|
||||
<ID>ModifierReused:EnvironmentCharts.kt$Box( contentAlignment = Alignment.TopStart, modifier = modifier.horizontalScroll(state = scrollState, reverseScrolling = true), ) { HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { graphColor }) TimeAxisOverlay(modifier = modifier.width(dp), oldest = oldest, newest = newest, selectedTime.lineInterval()) MetricPlottingCanvas( modifier = modifier.width(dp), telemetries = telemetries, graphData = graphData, selectedTime = selectedTime, oldest = oldest, timeDiff = timeDiff, rightMin = rightMin, rightMax = rightMax, ) }</ID>
|
||||
<ID>ModifierReused:EnvironmentCharts.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { graphColor })</ID>
|
||||
<ID>ModifierReused:EnvironmentCharts.kt$MetricPlottingCanvas( modifier = modifier.width(dp), telemetries = telemetries, graphData = graphData, selectedTime = selectedTime, oldest = oldest, timeDiff = timeDiff, rightMin = rightMin, rightMax = rightMax, )</ID>
|
||||
<ID>ModifierReused:EnvironmentCharts.kt$TimeAxisOverlay(modifier = modifier.width(dp), oldest = oldest, newest = newest, selectedTime.lineInterval())</ID>
|
||||
<ID>ModifierReused:PaxMetrics.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { Color.LightGray })</ID>
|
||||
<ID>ModifierReused:PaxMetrics.kt$Row(modifier = modifier.fillMaxWidth().fillMaxHeight(fraction = 0.33f)) { YAxisLabels( modifier = Modifier.weight(Y_AXIS_WEIGHT).fillMaxHeight().padding(start = 8.dp), labelColor = MaterialTheme.colorScheme.onSurface, minValue = minValue, maxValue = maxValue, ) Box( contentAlignment = Alignment.TopStart, modifier = Modifier.horizontalScroll(state = scrollState, reverseScrolling = true).weight(CHART_WEIGHT), ) { HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { Color.LightGray }) TimeAxisOverlay(modifier.width(dp), oldest = minTime, newest = maxTime, timeFrame.lineInterval()) Canvas(modifier = Modifier.width(dp).fillMaxHeight()) { val width = size.width val height = size.height fun xForTime(t: Int): Float = if (maxTime == minTime) width / 2 else (t - minTime).toFloat() / (maxTime - minTime) * width fun yForValue(v: Int): Float = height - (v - minValue) / (maxValue - minValue) * height fun drawLine(series: List<Pair<Int, Int>>, color: Color) { for (i in 1 until series.size) { drawLine( color = color, start = Offset(xForTime(series[i - 1].first), yForValue(series[i - 1].second)), end = Offset(xForTime(series[i].first), yForValue(series[i].second)), strokeWidth = 2.dp.toPx(), ) } } drawLine(bleSeries, PaxSeries.BLE.color) drawLine(wifiSeries, PaxSeries.WIFI.color) drawLine(totalSeries, PaxSeries.PAX.color) } } YAxisLabels( modifier = Modifier.weight(Y_AXIS_WEIGHT).fillMaxHeight().padding(end = 8.dp), labelColor = MaterialTheme.colorScheme.onSurface, minValue = minValue, maxValue = maxValue, ) }</ID>
|
||||
<ID>ModifierReused:PaxMetrics.kt$TimeAxisOverlay(modifier.width(dp), oldest = minTime, newest = maxTime, timeFrame.lineInterval())</ID>
|
||||
<ID>ModifierReused:PowerMetrics.kt$Canvas(modifier = modifier.width(dp)) { val width = size.width val height = size.height /* Voltage */ var index = 0 while (index < telemetries.size) { val path = Path() index = createPath( telemetries = telemetries, index = index, path = path, oldestTime = oldest.time, timeRange = timeDiff, width = width, timeThreshold = selectedTime.timeThreshold(), ) { i -> val telemetry = telemetries.getOrNull(i) ?: telemetries.last() val ratio = (retrieveVoltage(selectedChannel, telemetry) - voltageMin) / voltageDiff val y = height - (ratio * height) return@createPath y } drawPath( path = path, color = VOLTAGE_COLOR, style = Stroke(width = GraphUtil.RADIUS, cap = StrokeCap.Round), ) } /* Current */ index = 0 while (index < telemetries.size) { val path = Path() index = createPath( telemetries = telemetries, index = index, path = path, oldestTime = oldest.time, timeRange = timeDiff, width = width, timeThreshold = selectedTime.timeThreshold(), ) { i -> val telemetry = telemetries.getOrNull(i) ?: telemetries.last() val ratio = (retrieveCurrent(selectedChannel, telemetry) - Power.CURRENT.min) / currentDiff val y = height - (ratio * height) return@createPath y } drawPath( path = path, color = Power.CURRENT.color, style = Stroke(width = GraphUtil.RADIUS, cap = StrokeCap.Round), ) } }</ID>
|
||||
<ID>ModifierReused:PowerMetrics.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { graphColor })</ID>
|
||||
<ID>ModifierReused:PowerMetrics.kt$TimeAxisOverlay(modifier.width(dp), oldest = oldest.time, newest = newest.time, selectedTime.lineInterval())</ID>
|
||||
<ID>ModifierReused:PowerMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Power.CURRENT.color, minValue = Power.CURRENT.min, maxValue = Power.CURRENT.max, )</ID>
|
||||
<ID>ModifierReused:PowerMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), VOLTAGE_COLOR, minValue = voltageMin, maxValue = voltageMax, )</ID>
|
||||
<ID>ModifierReused:PreferenceCategory.kt$Card( modifier = modifier.padding(bottom = 8.dp), ) { Column( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { ProvideTextStyle(MaterialTheme.typography.bodyLarge) { content() } } }</ID>
|
||||
<ID>ModifierReused:PreferenceCategory.kt$Text( text, modifier = modifier.padding(start = 16.dp, top = 24.dp, bottom = 8.dp, end = 16.dp), style = MaterialTheme.typography.titleLarge, )</ID>
|
||||
<ID>ModifierReused:PreferenceFooter.kt$OutlinedButton( modifier = modifier .height(48.dp) .weight(1f), enabled = enabled, onClick = onPositiveClicked, ) { Text( text = stringResource(id = positiveText), ) }</ID>
|
||||
<ID>ModifierReused:PreferenceFooter.kt$OutlinedButton( modifier = modifier .height(48.dp) .weight(1f), onClick = onNegativeClicked, ) { Text( text = stringResource(id = negativeText), ) }</ID>
|
||||
<ID>ModifierReused:PreferenceFooter.kt$Row( modifier = modifier .fillMaxWidth() .height(64.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically, ) { OutlinedButton( modifier = modifier .height(48.dp) .weight(1f), onClick = onNegativeClicked, ) { Text( text = stringResource(id = negativeText), ) } OutlinedButton( modifier = modifier .height(48.dp) .weight(1f), enabled = enabled, onClick = onPositiveClicked, ) { Text( text = stringResource(id = positiveText), ) } }</ID>
|
||||
<ID>ModifierReused:SignalMetrics.kt$Canvas(modifier = modifier.width(dp)) { val width = size.width /* Plot */ for (packet in meshPackets) { val xRatio = (packet.rxTime - oldest.rxTime).toFloat() / timeDiff val x = xRatio * width /* SNR */ plotPoint( drawContext = drawContext, color = Metric.SNR.color, x = x, value = packet.rxSnr - Metric.SNR.min, divisor = snrDiff, ) /* RSSI */ plotPoint( drawContext = drawContext, color = Metric.RSSI.color, x = x, value = packet.rxRssi - Metric.RSSI.min, divisor = rssiDiff, ) } }</ID>
|
||||
<ID>ModifierReused:SignalMetrics.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { graphColor })</ID>
|
||||
<ID>ModifierReused:SignalMetrics.kt$TimeAxisOverlay( modifier.width(dp), oldest = oldest.rxTime, newest = newest.rxTime, selectedTime.lineInterval(), )</ID>
|
||||
<ID>ModifierReused:SignalMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Metric.RSSI.color, minValue = Metric.RSSI.min, maxValue = Metric.RSSI.max, )</ID>
|
||||
<ID>ModifierReused:SignalMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Metric.SNR.color, minValue = Metric.SNR.min, maxValue = Metric.SNR.max, )</ID>
|
||||
<ID>ModifierReused:TextDividerPreference.kt$Card( modifier = modifier.fillMaxWidth(), ) { Row( modifier = modifier .fillMaxWidth() .padding(all = 16.dp), verticalAlignment = Alignment.CenterVertically ) { Text( text = title, style = MaterialTheme.typography.bodyLarge, color = if (!enabled) { MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) } else { Color.Unspecified }, ) if (trailingIcon != null) { Icon( trailingIcon, "trailingIcon", modifier = modifier .fillMaxWidth() .wrapContentWidth(Alignment.End), ) } } }</ID>
|
||||
<ID>ModifierReused:TextDividerPreference.kt$Icon( trailingIcon, "trailingIcon", modifier = modifier .fillMaxWidth() .wrapContentWidth(Alignment.End), )</ID>
|
||||
<ID>ModifierReused:TextDividerPreference.kt$Row( modifier = modifier .fillMaxWidth() .padding(all = 16.dp), verticalAlignment = Alignment.CenterVertically ) { Text( text = title, style = MaterialTheme.typography.bodyLarge, color = if (!enabled) { MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) } else { Color.Unspecified }, ) if (trailingIcon != null) { Icon( trailingIcon, "trailingIcon", modifier = modifier .fillMaxWidth() .wrapContentWidth(Alignment.End), ) } }</ID>
|
||||
<ID>ModifierWithoutDefault:CommonCharts.kt$modifier</ID>
|
||||
<ID>ModifierWithoutDefault:EnvironmentCharts.kt$modifier</ID>
|
||||
<ID>MultiLineIfElse:Channel.kt$Channel$"Custom"</ID>
|
||||
<ID>MultiLineIfElse:Channel.kt$Channel$when (loraConfig.modemPreset) { ModemPreset.SHORT_TURBO -> "ShortTurbo" ModemPreset.SHORT_FAST -> "ShortFast" ModemPreset.SHORT_SLOW -> "ShortSlow" ModemPreset.MEDIUM_FAST -> "MediumFast" ModemPreset.MEDIUM_SLOW -> "MediumSlow" ModemPreset.LONG_FAST -> "LongFast" ModemPreset.LONG_SLOW -> "LongSlow" ModemPreset.LONG_MODERATE -> "LongMod" ModemPreset.VERY_LONG_SLOW -> "VLongSlow" else -> "Invalid" }</ID>
|
||||
<ID>MultiLineIfElse:EditTextPreference.kt$it.toDoubleOrNull()?.let { double -> valueState = it onValueChanged(double) }</ID>
|
||||
<ID>MultiLineIfElse:EditTextPreference.kt$it.toFloatOrNull()?.let { float -> valueState = it onValueChanged(float) }</ID>
|
||||
<ID>MultiLineIfElse:EditTextPreference.kt$it.toUIntOrNull()?.toInt()?.let { int -> valueState = it onValueChanged(int) }</ID>
|
||||
<ID>MultiLineIfElse:EditTextPreference.kt$onValueChanged(it)</ID>
|
||||
<ID>MultiLineIfElse:EditTextPreference.kt$valueState = it</ID>
|
||||
<ID>MultiLineIfElse:Exceptions.kt$Exceptions.errormsg("ignoring exception", ex)</ID>
|
||||
<ID>MultipleEmitters:CleanNodeDatabaseScreen.kt$NodesDeletionPreview</ID>
|
||||
<ID>MultipleEmitters:CommonCharts.kt$LegendLabel</ID>
|
||||
<ID>MultipleEmitters:DeviceMetrics.kt$DeviceMetricsChart</ID>
|
||||
<ID>MultipleEmitters:EditTextPreference.kt$EditTextPreference</ID>
|
||||
<ID>MultipleEmitters:EnvironmentCharts.kt$EnvironmentMetricsChart</ID>
|
||||
<ID>MultipleEmitters:NodeDetail.kt$EncryptionErrorContent</ID>
|
||||
<ID>MultipleEmitters:NodeDetail.kt$MetricsSection</ID>
|
||||
<ID>MultipleEmitters:PaxMetrics.kt$PaxMetricsChart</ID>
|
||||
<ID>MultipleEmitters:PowerMetrics.kt$PowerMetricsChart</ID>
|
||||
<ID>MultipleEmitters:PreferenceCategory.kt$PreferenceCategory</ID>
|
||||
<ID>MultipleEmitters:RadioConfig.kt$RadioConfigItemList</ID>
|
||||
<ID>MultipleEmitters:SignalMetrics.kt$SignalMetricsChart</ID>
|
||||
<ID>MutableStateAutoboxing:Contacts.kt$mutableStateOf(2)</ID>
|
||||
<ID>MutableStateParam:MessageList.kt$selectedIds</ID>
|
||||
<ID>NestedBlockDepth:LanguageUtils.kt$LanguageUtils$fun getLanguageTags(context: Context): Map<String, String></ID>
|
||||
<ID>NestedBlockDepth:MeshService.kt$MeshService$private fun handleReceivedAdmin(fromNodeNum: Int, a: AdminProtos.AdminMessage)</ID>
|
||||
<ID>NestedBlockDepth:MeshService.kt$MeshService$private fun handleReceivedData(packet: MeshPacket)</ID>
|
||||
<ID>NestedBlockDepth:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)</ID>
|
||||
<ID>NewLineAtEndOfFile:BLEException.kt$com.geeksville.mesh.service.BLEException.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:BluetoothInterfaceFactory.kt$com.geeksville.mesh.repository.radio.BluetoothInterfaceFactory.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:BluetoothRepositoryModule.kt$com.geeksville.mesh.repository.bluetooth.BluetoothRepositoryModule.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:BootCompleteReceiver.kt$com.geeksville.mesh.service.BootCompleteReceiver.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:CoroutineDispatchers.kt$com.geeksville.mesh.CoroutineDispatchers.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:Coroutines.kt$com.geeksville.mesh.concurrent.Coroutines.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:DateUtils.kt$com.geeksville.mesh.android.DateUtils.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:DebugLogFile.kt$com.geeksville.mesh.android.DebugLogFile.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:DeferredExecution.kt$com.geeksville.mesh.concurrent.DeferredExecution.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:DeviceVersion.kt$com.geeksville.mesh.model.DeviceVersion.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:InterfaceId.kt$com.geeksville.mesh.repository.radio.InterfaceId.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:InterfaceSpec.kt$com.geeksville.mesh.repository.radio.InterfaceSpec.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:MockInterfaceFactory.kt$com.geeksville.mesh.repository.radio.MockInterfaceFactory.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:NopInterface.kt$com.geeksville.mesh.repository.radio.NopInterface.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:NopInterfaceFactory.kt$com.geeksville.mesh.repository.radio.NopInterfaceFactory.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:ProbeTableProvider.kt$com.geeksville.mesh.repository.usb.ProbeTableProvider.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:QuickChatActionRepository.kt$com.geeksville.mesh.database.QuickChatActionRepository.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:RadioNotConnectedException.kt$com.geeksville.mesh.service.RadioNotConnectedException.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:RegularPreference.kt$com.geeksville.mesh.ui.common.components.RegularPreference.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:SerialConnection.kt$com.geeksville.mesh.repository.usb.SerialConnection.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:SerialConnectionListener.kt$com.geeksville.mesh.repository.usb.SerialConnectionListener.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:SerialInterface.kt$com.geeksville.mesh.repository.radio.SerialInterface.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:SerialInterfaceFactory.kt$com.geeksville.mesh.repository.radio.SerialInterfaceFactory.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:TCPInterfaceFactory.kt$com.geeksville.mesh.repository.radio.TCPInterfaceFactory.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:UsbBroadcastReceiver.kt$com.geeksville.mesh.repository.usb.UsbBroadcastReceiver.kt</ID>
|
||||
<ID>NewLineAtEndOfFile:UsbRepositoryModule.kt$com.geeksville.mesh.repository.usb.UsbRepositoryModule.kt</ID>
|
||||
<ID>NoBlankLineBeforeRbrace:DebugLogFile.kt$BinaryLogFile$ </ID>
|
||||
<ID>NoBlankLineBeforeRbrace:NopInterface.kt$NopInterface$ </ID>
|
||||
<ID>NoConsecutiveBlankLines:BootCompleteReceiver.kt$ </ID>
|
||||
<ID>NoConsecutiveBlankLines:Constants.kt$ </ID>
|
||||
<ID>NoConsecutiveBlankLines:DebugLogFile.kt$ </ID>
|
||||
<ID>NoConsecutiveBlankLines:DeferredExecution.kt$ </ID>
|
||||
<ID>NoConsecutiveBlankLines:Exceptions.kt$ </ID>
|
||||
<ID>NoConsecutiveBlankLines:IRadioInterface.kt$ </ID>
|
||||
<ID>NoEmptyClassBody:DebugLogFile.kt$BinaryLogFile${ }</ID>
|
||||
<ID>NoSemicolons:DateUtils.kt$DateUtils$;</ID>
|
||||
<ID>NoWildcardImports:UsbRepository.kt$import kotlinx.coroutines.flow.*</ID>
|
||||
<ID>OptionalAbstractKeyword:SyncContinuation.kt$Continuation$abstract</ID>
|
||||
<ID>ParameterNaming:AmbientLightingConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:AudioConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:BitwisePreference.kt$onItemSelected</ID>
|
||||
<ID>ParameterNaming:BluetoothConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:CannedMessageConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:ChannelSettingsItemList.kt$onPositiveClicked</ID>
|
||||
<ID>ParameterNaming:ChannelSettingsItemList.kt$onSelected</ID>
|
||||
<ID>ParameterNaming:CleanNodeDatabaseScreen.kt$onCheckedChanged</ID>
|
||||
<ID>ParameterNaming:CleanNodeDatabaseScreen.kt$onDaysChanged</ID>
|
||||
<ID>ParameterNaming:Contacts.kt$onDeleteSelected</ID>
|
||||
<ID>ParameterNaming:Contacts.kt$onMuteSelected</ID>
|
||||
<ID>ParameterNaming:DetectionSensorConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:DeviceConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:DisplayConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:DropDownPreference.kt$onItemSelected</ID>
|
||||
<ID>ParameterNaming:EditIPv4Preference.kt$onValueChanged</ID>
|
||||
<ID>ParameterNaming:EditListPreference.kt$onValuesChanged</ID>
|
||||
<ID>ParameterNaming:EditPasswordPreference.kt$onValueChanged</ID>
|
||||
<ID>ParameterNaming:EditTextPreference.kt$onValueChanged</ID>
|
||||
<ID>ParameterNaming:ExternalNotificationConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:LoRaConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:MQTTConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:MQTTConfigItemList.kt$onShouldReportLocationChanged</ID>
|
||||
<ID>ParameterNaming:MapReportingPreference.kt$onMapReportingEnabledChanged</ID>
|
||||
<ID>ParameterNaming:MapReportingPreference.kt$onPositionPrecisionChanged</ID>
|
||||
<ID>ParameterNaming:MapReportingPreference.kt$onPublishIntervalSecsChanged</ID>
|
||||
<ID>ParameterNaming:MapReportingPreference.kt$onShouldReportLocationChanged</ID>
|
||||
<ID>ParameterNaming:MessageList.kt$onUnreadChanged</ID>
|
||||
<ID>ParameterNaming:NeighborInfoConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:NetworkConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:NodeDetail.kt$onFirmwareSelected</ID>
|
||||
<ID>ParameterNaming:NodeFilterTextField.kt$onToggleShowIgnored</ID>
|
||||
<ID>ParameterNaming:PaxcounterConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:PositionConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:PositionPrecisionPreference.kt$onValueChanged</ID>
|
||||
<ID>ParameterNaming:PowerConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:PreferenceFooter.kt$onCancelClicked</ID>
|
||||
<ID>ParameterNaming:PreferenceFooter.kt$onNegativeClicked</ID>
|
||||
<ID>ParameterNaming:PreferenceFooter.kt$onPositiveClicked</ID>
|
||||
<ID>ParameterNaming:PreferenceFooter.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:RangeTestConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:RemoteHardwareConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:SerialConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:SlidingSelector.kt$onOptionSelected</ID>
|
||||
<ID>ParameterNaming:StoreForwardConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:TelemetryConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:UsbDevices.kt$onDeviceSelected</ID>
|
||||
<ID>ParameterNaming:UserConfigItemList.kt$onSaveClicked</ID>
|
||||
<ID>ParameterNaming:WelcomeScreen.kt$onGetStarted</ID>
|
||||
<ID>PreviewAnnotationNaming:LargeFontPreview.kt$LargeFontPreview$LargeFontPreview</ID>
|
||||
<ID>PreviewPublic:BatteryInfo.kt$BatteryInfoPreview</ID>
|
||||
<ID>PreviewPublic:BatteryInfo.kt$BatteryInfoPreviewSimple</ID>
|
||||
<ID>PreviewPublic:Channel.kt$ModemPresetInfoPreview</ID>
|
||||
<ID>PreviewPublic:ElevationInfo.kt$ElevationInfoPreview</ID>
|
||||
<ID>PreviewPublic:EmptyStateContent.kt$EmptyStateContentPreview</ID>
|
||||
<ID>PreviewPublic:IndoorAirQuality.kt$IAQScalePreview</ID>
|
||||
<ID>PreviewPublic:LastHeardInfo.kt$LastHeardInfoPreview</ID>
|
||||
<ID>PreviewPublic:LazyColumnDragAndDropDemo.kt$LazyColumnDragAndDropDemo</ID>
|
||||
<ID>PreviewPublic:LinkedCoordinates.kt$LinkedCoordinatesPreview</ID>
|
||||
<ID>PreviewPublic:MapReportingPreference.kt$MapReportingPreview</ID>
|
||||
<ID>PreviewPublic:MaterialBatteryInfo.kt$MaterialBatteryInfoPreview</ID>
|
||||
<ID>PreviewPublic:NodeChip.kt$NodeChipPreview</ID>
|
||||
<ID>PreviewPublic:NodeItem.kt$NodeInfoPreview</ID>
|
||||
<ID>PreviewPublic:NodeItem.kt$NodeInfoSimplePreview</ID>
|
||||
<ID>PreviewPublic:NodeStatusIcons.kt$StatusIconsPreview</ID>
|
||||
<ID>PreviewPublic:Reaction.kt$ReactionItemPreview</ID>
|
||||
<ID>PreviewPublic:Reaction.kt$ReactionRowPreview</ID>
|
||||
<ID>PreviewPublic:SatelliteCountInfo.kt$SatelliteCountInfoPreview</ID>
|
||||
<ID>PreviewPublic:SignalInfo.kt$SignalInfoPreview</ID>
|
||||
<ID>PreviewPublic:SignalInfo.kt$SignalInfoSelfPreview</ID>
|
||||
<ID>PreviewPublic:SignalInfo.kt$SignalInfoSimplePreview</ID>
|
||||
<ID>PreviewPublic:SlidingSelector.kt$SlidingSelectorPreview</ID>
|
||||
<ID>RethrowCaughtException:SyncContinuation.kt$Continuation$throw ex</ID>
|
||||
<ID>ReturnCount:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)</ID>
|
||||
<ID>SpacingAroundKeyword:Exceptions.kt$if</ID>
|
||||
<ID>SpacingAroundKeyword:Exceptions.kt$when</ID>
|
||||
<ID>SpacingAroundRangeOperator:BatteryInfo.kt$..</ID>
|
||||
<ID>SwallowedException:BluetoothInterface.kt$BluetoothInterface$ex: CancellationException</ID>
|
||||
<ID>SwallowedException:ChannelSet.kt$ex: Throwable</ID>
|
||||
<ID>SwallowedException:DeviceVersion.kt$DeviceVersion$e: Exception</ID>
|
||||
<ID>SwallowedException:Exceptions.kt$ex: Throwable</ID>
|
||||
<ID>SwallowedException:MeshLog.kt$MeshLog$e: IOException</ID>
|
||||
<ID>SwallowedException:MeshService.kt$MeshService$ex: BLEException</ID>
|
||||
<ID>SwallowedException:MeshService.kt$MeshService$ex: CancellationException</ID>
|
||||
<ID>SwallowedException:NsdManager.kt$ex: IllegalArgumentException</ID>
|
||||
<ID>SwallowedException:SafeBluetooth.kt$SafeBluetooth$ex: DeadObjectException</ID>
|
||||
<ID>SwallowedException:SafeBluetooth.kt$SafeBluetooth$ex: NullPointerException</ID>
|
||||
<ID>SwallowedException:ServiceClient.kt$ServiceClient$ex: IllegalArgumentException</ID>
|
||||
<ID>SwallowedException:TCPInterface.kt$TCPInterface$ex: SocketTimeoutException</ID>
|
||||
<ID>TooGenericExceptionCaught:BTScanModel.kt$BTScanModel$ex: Throwable</ID>
|
||||
<ID>TooGenericExceptionCaught:BluetoothInterface.kt$BluetoothInterface$ex: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:ChannelSet.kt$ex: Throwable</ID>
|
||||
<ID>TooGenericExceptionCaught:DeviceVersion.kt$DeviceVersion$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:Exceptions.kt$ex: Throwable</ID>
|
||||
<ID>TooGenericExceptionCaught:LanguageUtils.kt$LanguageUtils$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:LocationRepository.kt$LocationRepository$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:MQTTRepository.kt$MQTTRepository$ex: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:MeshService.kt$MeshService$ex: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:MeshService.kt$MeshService.<no name provided>$ex: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:MeshServiceStarter.kt$ServiceStarter$ex: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:RadioConfigViewModel.kt$RadioConfigViewModel$ex: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:SafeBluetooth.kt$SafeBluetooth$ex: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:SafeBluetooth.kt$SafeBluetooth$ex: NullPointerException</ID>
|
||||
<ID>TooGenericExceptionCaught:SyncContinuation.kt$Continuation$ex: Throwable</ID>
|
||||
<ID>TooGenericExceptionCaught:TCPInterface.kt$TCPInterface$ex: Throwable</ID>
|
||||
<ID>TooGenericExceptionThrown:DeviceVersion.kt$DeviceVersion$throw Exception("Can't parse version $s")</ID>
|
||||
<ID>TooGenericExceptionThrown:MeshService.kt$MeshService$throw Exception("Can't set user without a NodeInfo")</ID>
|
||||
<ID>TooGenericExceptionThrown:MeshService.kt$MeshService.<no name provided>$throw Exception("Port numbers must be non-zero!")</ID>
|
||||
<ID>TooGenericExceptionThrown:ServiceClient.kt$ServiceClient$throw Exception("Haven't called connect")</ID>
|
||||
<ID>TooGenericExceptionThrown:ServiceClient.kt$ServiceClient$throw Exception("Service not bound")</ID>
|
||||
<ID>TooGenericExceptionThrown:SyncContinuation.kt$SyncContinuation$throw Exception("SyncContinuation timeout")</ID>
|
||||
<ID>TooGenericExceptionThrown:SyncContinuation.kt$SyncContinuation$throw Exception("This shouldn't happen")</ID>
|
||||
<ID>TooManyFunctions:BluetoothInterface.kt$BluetoothInterface : IRadioInterfaceLogging</ID>
|
||||
<ID>TooManyFunctions:MeshService.kt$MeshService : ServiceLogging</ID>
|
||||
<ID>TooManyFunctions:MeshService.kt$MeshService$<no name provided> : Stub</ID>
|
||||
<ID>TooManyFunctions:NodeDetail.kt$com.geeksville.mesh.ui.node.NodeDetail.kt</ID>
|
||||
<ID>TooManyFunctions:PacketDao.kt$PacketDao</ID>
|
||||
<ID>TooManyFunctions:PacketRepository.kt$PacketRepository</ID>
|
||||
<ID>TooManyFunctions:RadioConfigRepository.kt$RadioConfigRepository</ID>
|
||||
<ID>TooManyFunctions:RadioConfigViewModel.kt$RadioConfigViewModel : ViewModelLogging</ID>
|
||||
<ID>TooManyFunctions:RadioInterfaceService.kt$RadioInterfaceService : Logging</ID>
|
||||
<ID>TooManyFunctions:SafeBluetooth.kt$SafeBluetooth : LoggingCloseable</ID>
|
||||
<ID>TooManyFunctions:UIState.kt$UIViewModel : ViewModelLogging</ID>
|
||||
<ID>TopLevelPropertyNaming:Constants.kt$const val prefix = "com.geeksville.mesh"</ID>
|
||||
<ID>UtilityClassWithPublicConstructor:NetworkRepositoryModule.kt$NetworkRepositoryModule</ID>
|
||||
<ID>ViewModelForwarding:Main.kt$MainAppBar( viewModel = uIViewModel, navController = navController, onAction = { action -> when (action) { is NodeMenuAction.MoreDetails -> { navController.navigate( NodesRoutes.NodeDetailGraph(action.node.num), { launchSingleTop = true restoreState = true }, ) } is NodeMenuAction.Share -> sharedContact = action.node else -> {} } }, )</ID>
|
||||
<ID>ViewModelForwarding:Main.kt$NavGraph( modifier = Modifier.fillMaxSize().recalculateWindowInsets().safeDrawingPadding().imePadding(), uIViewModel = uIViewModel, bluetoothViewModel = bluetoothViewModel, navController = navController, )</ID>
|
||||
<ID>ViewModelForwarding:Main.kt$ScannedQrCodeDialog(uIViewModel, newChannelSet)</ID>
|
||||
<ID>ViewModelForwarding:Main.kt$VersionChecks(uIViewModel)</ID>
|
||||
<ID>ViewModelForwarding:Message.kt$MessageList( modifier = Modifier.fillMaxSize(), listState = listState, messages = messages, selectedIds = selectedMessageIds, onUnreadChanged = { messageId -> onEvent(MessageScreenEvent.ClearUnreadCount(messageId)) }, onSendReaction = { emoji, id -> onEvent(MessageScreenEvent.SendReaction(emoji, id)) }, viewModel = viewModel, contactKey = contactKey, onReply = { message -> replyingToPacketId = message?.packetId }, onNodeMenuAction = { action -> onEvent(MessageScreenEvent.HandleNodeMenuAction(action)) }, )</ID>
|
||||
<ID>ViewModelForwarding:NodeDetail.kt$NodeDetailContent( node = node, ourNode = ourNode, metricsState = state, lastTracerouteTime = lastTracerouteTime, availableLogs = availableLogs, uiViewModel = uiViewModel, onAction = { action -> handleNodeAction( action = action, uiViewModel = uiViewModel, node = node, navigateToMessages = navigateToMessages, onNavigateUp = onNavigateUp, onNavigate = onNavigate, viewModel = viewModel, ) }, modifier = modifier, )</ID>
|
||||
<ID>ViewModelForwarding:NodeScreen.kt$AddContactFAB( modifier = Modifier.animateFloatingActionButton( visible = !isScrollInProgress && connectionState == ConnectionState.CONNECTED && shareCapable, alignment = Alignment.BottomEnd, ), model = model, onSharedContactImport = { contact -> model.addSharedContact(contact) }, )</ID>
|
||||
<ID>ViewModelInjection:DebugSearch.kt$viewModel</ID>
|
||||
<ID>WildcardImport:UsbRepository.kt$import kotlinx.coroutines.flow.*</ID>
|
||||
<ID>Wrapping:DebugFilters.kt$(</ID>
|
||||
<ID>Wrapping:DebugFilters.kt$if (filter in filterTexts) { Icon( imageVector = Icons.Filled.Done, contentDescription = stringResource(id = R.string.debug_filter_included), ) }</ID>
|
||||
<ID>Wrapping:EditTextPreference.kt$;</ID>
|
||||
<ID>Wrapping:MQTTRepository.kt$MQTTRepository.<no name provided>$(</ID>
|
||||
<ID>Wrapping:Message.kt${ event -> when (event) { is MessageScreenEvent.SendMessage -> { viewModel.sendMessage(event.text, contactKey, event.replyingToPacketId) if (event.replyingToPacketId != null) replyingToPacketId = null messageInputState.clearText() } is MessageScreenEvent.SendReaction -> viewModel.sendReaction(event.emoji, event.messageId, contactKey) is MessageScreenEvent.DeleteMessages -> { viewModel.deleteMessages(event.ids) selectedMessageIds.value = emptySet() showDeleteDialog = false } is MessageScreenEvent.ClearUnreadCount -> viewModel.clearUnreadCount(contactKey, event.lastReadMessageId) is MessageScreenEvent.HandleNodeMenuAction -> { when (val action = event.action) { is NodeMenuAction.DirectMessage -> { val hasPKC = ourNode?.hasPKC == true && action.node.hasPKC val targetChannel = if (hasPKC) { DataPacket.PKC_CHANNEL_INDEX } else { action.node.channel } navigateToMessages("$targetChannel${action.node.user.id}") } is NodeMenuAction.MoreDetails -> navigateToNodeDetails(action.node.num) is NodeMenuAction.Share -> sharedContact = action.node else -> viewModel.handleNodeMenuAction(action) } } is MessageScreenEvent.SetTitle -> viewModel.setTitle(event.title) is MessageScreenEvent.NavigateToMessages -> navigateToMessages(event.contactKey) is MessageScreenEvent.NavigateToNodeDetails -> navigateToNodeDetails(event.nodeNum) MessageScreenEvent.NavigateBack -> onNavigateBack() is MessageScreenEvent.CopyToClipboard -> { clipboardManager.nativeClipboard.setPrimaryClip(ClipData.newPlainText(event.text, event.text)) selectedMessageIds.value = emptySet() } } }</ID>
|
||||
<ID>Wrapping:SerialConnectionImpl.kt$SerialConnectionImpl$(</ID>
|
||||
<ID>Wrapping:SerialConnectionImpl.kt$SerialConnectionImpl$(port, object : SerialInputOutputManager.Listener { override fun onNewData(data: ByteArray) { listener.onDataReceived(data) } override fun onRunError(e: Exception?) { closed.set(true) ignoreException { port.dtr = false port.rts = false port.close() } closedLatch.countDown() listener.onDisconnected(e) } })</ID>
|
||||
<ID>Wrapping:SerialInterface.kt$SerialInterface$(</ID>
|
||||
<ID>Wrapping:SerialInterface.kt$SerialInterface$(device, object : SerialConnectionListener { override fun onMissingPermission() { errormsg("Need permissions for port") } override fun onConnected() { onConnect.invoke() } override fun onDataReceived(bytes: ByteArray) { debug("Received ${bytes.size} byte(s)") bytes.forEach(::readChar) } override fun onDisconnected(thrown: Exception?) { thrown?.let { e -> errormsg("Serial error: $e") } debug("$device disconnected") onDeviceDisconnect(false) } })</ID>
|
||||
<ID>Wrapping:ServiceClient.kt$ServiceClient$Closeable, Logging</ID>
|
||||
<ID>Wrapping:SlidingSelector.kt$;</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) $YEAR Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -15,10 +15,3 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
16
config/spotless/copyright.kts
Normal file
16
config/spotless/copyright.kts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) $YEAR Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) $YEAR Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
|||
17
config/spotless/copyright.xml
Normal file
17
config/spotless/copyright.xml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) $YEAR Meshtastic LLC
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
|
@ -30,9 +30,9 @@ org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
|||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
org.gradle.parallel=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.configureondemand=false
|
||||
|
||||
# Enable caching between builds.
|
||||
org.gradle.caching=true
|
||||
|
|
@ -52,8 +52,9 @@ kotlin.code.style=official
|
|||
|
||||
# Disable build features that are enabled by default,
|
||||
# https://developer.android.com/build/releases/gradle-plugin#default-changes
|
||||
android.nonTransitiveRClass=false
|
||||
android.nonFinalResIds=false
|
||||
android.nonTransitiveRClass=true
|
||||
|
||||
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
|
||||
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true
|
||||
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true
|
||||
|
||||
dependency.analysis.print.build.health=true
|
||||
|
|
@ -1,170 +1,156 @@
|
|||
#[allow(unused)]
|
||||
[versions]
|
||||
accompanistPermissions = "0.37.3"
|
||||
adaptive = "1.2.0-beta02"
|
||||
adaptive-navigation-suite = "1.3.2"
|
||||
agp = "8.13.0"
|
||||
androidDocumentationPlugin = "2.0.0"
|
||||
androidxComposeMaterial3Adaptive = "1.2.0-beta02"
|
||||
appcompat = "1.7.1"
|
||||
awesome-app-rating = "2.8.0"
|
||||
coil = "3.3.0"
|
||||
compose-bom = "2025.09.00"
|
||||
constraintlayout = "2.2.1"
|
||||
core-ktx = "1.17.0"
|
||||
core-location-altitude = "1.0.0-alpha03"
|
||||
core-splashscreen = "1.0.1"
|
||||
crashlytics = "3.0.6"
|
||||
datastore = "1.1.7"
|
||||
dd-sdk-android = "3.0.0"
|
||||
dd-sdk-android-gradle-plugin = "1.19.0"
|
||||
detekt = "1.23.8"
|
||||
devtools-ksp = "2.2.20-2.0.2"
|
||||
emoji2 = "1.6.0"
|
||||
espresso-core = "3.7.0"
|
||||
firebase-bom = "34.2.0"
|
||||
google-services = "4.4.3"
|
||||
hilt = "2.57.1"
|
||||
hilt-navigation-compose = "1.3.0"
|
||||
junit = "4.13.2"
|
||||
junit-version = "1.3.0"
|
||||
kotlin = "2.2.20"
|
||||
kotlinx-collections-immutable = "0.4.0"
|
||||
kotlinx-coroutines-android = "1.10.2"
|
||||
kotlinx-serialization-json = "1.9.0"
|
||||
kover = "0.9.1"
|
||||
lifecycle = "2.9.3"
|
||||
location-services = "21.3.0"
|
||||
maps-compose = "6.10.0"
|
||||
markdownRenderer = "0.35.0"
|
||||
material = "1.13.0"
|
||||
material3 = "1.5.0-alpha04"
|
||||
mgrs = "2.1.3"
|
||||
navigation = "2.9.4"
|
||||
navigation3 = "1.0.0-alpha09"
|
||||
okhttp = "5.1.0"
|
||||
org-eclipse-paho-client-mqttv3 = "1.2.5"
|
||||
osmbonuspack = "6.9.0"
|
||||
osmdroid-android = "6.1.20"
|
||||
protobuf-gradle-plugin = "0.9.5"
|
||||
protobuf-kotlin = "4.32.1"
|
||||
protobuf = "4.32.1"
|
||||
retrofit = "3.0.0"
|
||||
room = "2.8.0"
|
||||
secrets-gradle-plugin = "2.0.1"
|
||||
streamsupport-minifuture = "1.7.4"
|
||||
timber = "5.0.1"
|
||||
usb-serial-android = "3.9.0"
|
||||
work-runtime-ktx = "2.10.4"
|
||||
zxing-android-embedded = "4.3.0"
|
||||
zxing-core = "3.5.3"
|
||||
spotless = "7.2.1"
|
||||
dokka = "2.0.0"
|
||||
|
||||
[libraries]
|
||||
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
|
||||
activity = { group = "androidx.activity", name = "activity" }
|
||||
actvity-ktx = { group = "androidx.activity", name = "activity-ktx" }
|
||||
activity-compose = { group = "androidx.activity", name = "activity-compose" }
|
||||
adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive", version.ref = "adaptive" }
|
||||
adaptive-layout = { group = "androidx.compose.material3.adaptive", name = "adaptive-layout", version.ref = "adaptive" }
|
||||
adaptive-navigation = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation", version.ref = "adaptive" }
|
||||
adaptive-navigation-android = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation-android", version.ref = "adaptive" }
|
||||
adaptive-navigation-suite = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite", version.ref = "adaptive-navigation-suite" }
|
||||
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||
appcompat-resources = { group = "androidx.appcompat", name = "appcompat-resources", version.ref = "appcompat" }
|
||||
awesome-app-rating = { group = "com.suddenh4x.ratingdialog", name = "awesome-app-rating", version.ref = "awesome-app-rating" }
|
||||
coil = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" }
|
||||
coil-network-core = { group = "io.coil-kt.coil3", name = "coil-network-core", version.ref = "coil" }
|
||||
coil-network-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version.ref = "coil" }
|
||||
coil-svg = { group = "io.coil-kt.coil3", name = "coil-svg", version.ref = "coil" }
|
||||
compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
||||
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
||||
compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
|
||||
compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata" }
|
||||
compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||
compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
|
||||
core-location-altitude = { group = "androidx.core", name = "core-location-altitude", version.ref = "core-location-altitude" }
|
||||
core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "core-splashscreen" }
|
||||
datastore = { group = "androidx.datastore", name = "datastore", version.ref = "datastore" }
|
||||
datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" }
|
||||
dd-sdk-android-compose = { group = "com.datadoghq", name = "dd-sdk-android-compose", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-logs = { group = "com.datadoghq", name = "dd-sdk-android-logs", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-okhttp = { group = "com.datadoghq", name = "dd-sdk-android-okhttp", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-rum = { group = "com.datadoghq", name = "dd-sdk-android-rum", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-session-replay = { group = "com.datadoghq", name = "dd-sdk-android-session-replay", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-session-replay-compose = { group = "com.datadoghq", name = "dd-sdk-android-session-replay-compose", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-timber = { group = "com.datadoghq", name = "dd-sdk-android-timber", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-trace = { group = "com.datadoghq", name = "dd-sdk-android-trace", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-trace-otel = { group = "com.datadoghq", name = "dd-sdk-android-trace-otel", version.ref = "dd-sdk-android" }
|
||||
detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" }
|
||||
dokka-android-documentation-plugin = { module = "org.jetbrains.dokka:android-documentation-plugin", version.ref = "androidDocumentationPlugin" }
|
||||
emoji2-emojipicker = { group = "androidx.emoji2", name = "emoji2-emojipicker", version.ref = "emoji2" }
|
||||
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
|
||||
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit-version" }
|
||||
firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics" }
|
||||
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" }
|
||||
firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics" }
|
||||
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
|
||||
hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt" }
|
||||
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }
|
||||
hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hilt-navigation-compose" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
kotlinx-collections-immutable = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "kotlinx-collections-immutable" }
|
||||
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines-android" }
|
||||
kotlinx-coroutines-guava = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-guava", version.ref = "kotlinx-coroutines-android" }
|
||||
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
|
||||
lifecycle-common-java8 = { group = "androidx.lifecycle", name = "lifecycle-common-java8", version.ref = "lifecycle" }
|
||||
lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" }
|
||||
lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "lifecycle" }
|
||||
lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
|
||||
lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
||||
lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
|
||||
lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
|
||||
location-services = { group = "com.google.android.gms", name = "play-services-location", version.ref = "location-services" }
|
||||
maps-compose = { group = "com.google.maps.android", name = "maps-compose", version.ref = "maps-compose" }
|
||||
maps-compose-utils = { group = "com.google.maps.android", name = "maps-compose-utils", version.ref = "maps-compose" }
|
||||
maps-compose-widgets = { group = "com.google.maps.android", name = "maps-compose-widgets", version.ref = "maps-compose" }
|
||||
markdown-renderer = { group = "com.mikepenz", name = "multiplatform-markdown-renderer", version.ref = "markdownRenderer" }
|
||||
markdown-renderer-m3 = { group = "com.mikepenz", name = "multiplatform-markdown-renderer-m3", version.ref = "markdownRenderer" }
|
||||
markdown-renderer-android = { group = "com.mikepenz", name = "multiplatform-markdown-renderer-android", version.ref = "markdownRenderer" }
|
||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||
mgrs = { group = "mil.nga", name = "mgrs", version.ref = "mgrs" }
|
||||
navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
|
||||
navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "navigation" }
|
||||
navigation3-runtime = { group = "androidx.navigation3", name = "navigation3-runtime", version.ref = "navigation3" }
|
||||
navigation3-ui = { group = "androidx.navigation3", name = "navigation3-ui", version.ref = "navigation3" }
|
||||
okhttp3 = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
|
||||
okhttp3-logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
|
||||
org-eclipse-paho-client-mqttv3 = { group = "org.eclipse.paho", name = "org.eclipse.paho.client.mqttv3", version.ref = "org-eclipse-paho-client-mqttv3" }
|
||||
osmbonuspack = { group = "com.github.MKergall", name = "osmbonuspack", version.ref = "osmbonuspack" }
|
||||
osmdroid-android = { group = "org.osmdroid", name = "osmdroid-android", version.ref = "osmdroid-android" }
|
||||
osmdroid-geopackage = { group = "org.osmdroid", name = "osmdroid-geopackage", version.ref = "osmdroid-android" }
|
||||
protobuf-kotlin = { group = "com.google.protobuf", name = "protobuf-kotlin", version.ref = "protobuf-kotlin" }
|
||||
protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf-kotlin" }
|
||||
retrofit2 = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
|
||||
retrofit2-kotlin-serialization = { group = "com.squareup.retrofit2", name = "converter-kotlinx-serialization", version.ref = "retrofit" }
|
||||
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
||||
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
||||
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
||||
room-testing = { group = "androidx.room", name = "room-testing", version.ref = "room" }
|
||||
streamsupport-minifuture = { group = "net.sourceforge.streamsupport", name = "streamsupport-minifuture", version.ref = "streamsupport-minifuture" }
|
||||
timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" }
|
||||
usb-serial-android = { group = "com.github.mik3y", name = "usb-serial-for-android", version.ref = "usb-serial-android" }
|
||||
work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work-runtime-ktx" }
|
||||
zxing-android-embedded = { group = "com.journeyapps", name = "zxing-android-embedded", version.ref = "zxing-android-embedded" }
|
||||
zxing-core = { group = "com.google.zxing", name = "core", version.ref = "zxing-core" }
|
||||
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version = "0.37.3" }
|
||||
activity = { module = "androidx.activity:activity" }
|
||||
actvity-ktx = { module = "androidx.activity:activity-ktx" }
|
||||
activity-compose = { module = "androidx.activity:activity-compose" }
|
||||
android-desugarJdkLibs = { module = "com.android.tools:desugar_jdk_libs", version = "2.1.5" }
|
||||
androidx-tracing-ktx = { module = "androidx.tracing:tracing-ktx", version = "1.3.0" }
|
||||
androidx-compose-bom = { module = "androidx.compose:compose-bom-alpha", version = "2025.08.01" }
|
||||
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation", version = "1.8.0-alpha07" }
|
||||
androidx-compose-foundation-layout = { module = "androidx.compose.foundation:foundation-layout" }
|
||||
androidx-compose-material-iconsExtended = { module = "androidx.compose.material:material-icons-extended" }
|
||||
androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
|
||||
androidx-compose-material3-navigationSuite = { module = "androidx.compose.material3:material3-adaptive-navigation-suite" }
|
||||
androidx-compose-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive", version.ref = "androidxComposeMaterial3Adaptive" }
|
||||
androidx-compose-material3-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout", version.ref = "androidxComposeMaterial3Adaptive" }
|
||||
androidx-compose-material3-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation", version.ref = "androidxComposeMaterial3Adaptive" }
|
||||
androidx-compose-material3-windowSizeClass = { module = "androidx.compose.material3:material3-window-size-class" }
|
||||
androidx-compose-runtime = { module = "androidx.compose.runtime:runtime" }
|
||||
androidx-compose-runtime-tracing = { module = "androidx.compose.runtime:runtime-tracing", version = "1.7.6" }
|
||||
androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4" }
|
||||
androidx-compose-ui-testManifest = { module = "androidx.compose.ui:ui-test-manifest" }
|
||||
androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
|
||||
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
|
||||
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
|
||||
appcompat-resources = { module = "androidx.appcompat:appcompat-resources", version.ref = "appcompat" }
|
||||
awesome-app-rating = { module = "com.suddenh4x.ratingdialog:awesome-app-rating", version = "2.8.0" }
|
||||
coil = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||
coil-network-core = { module = "io.coil-kt.coil3:coil-network-core", version.ref = "coil" }
|
||||
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
||||
coil-svg = { module = "io.coil-kt.coil3:coil-svg", version.ref = "coil" }
|
||||
compose-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata" }
|
||||
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.2.1" }
|
||||
core-ktx = { module = "androidx.core:core-ktx", version = "1.17.0" }
|
||||
core-location-altitude = { module = "androidx.core:core-location-altitude", version = "1.0.0-alpha03" }
|
||||
core-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.0.1" }
|
||||
datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" }
|
||||
datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" }
|
||||
dd-sdk-android-compose = { module = "com.datadoghq:dd-sdk-android-compose", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-logs = { module = "com.datadoghq:dd-sdk-android-logs", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-okhttp = { module = "com.datadoghq:dd-sdk-android-okhttp", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-rum = { module = "com.datadoghq:dd-sdk-android-rum", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-session-replay = { module = "com.datadoghq:dd-sdk-android-session-replay", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-session-replay-compose = { module = "com.datadoghq:dd-sdk-android-session-replay-compose", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-timber = { module = "com.datadoghq:dd-sdk-android-timber", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-trace = { module = "com.datadoghq:dd-sdk-android-trace", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-trace-otel = { module = "com.datadoghq:dd-sdk-android-trace-otel", version.ref = "dd-sdk-android" }
|
||||
dokka-android-documentation-plugin = { module = "org.jetbrains.dokka:android-documentation-plugin", version = "2.0.0" }
|
||||
emoji2-emojipicker = { module = "androidx.emoji2:emoji2-emojipicker", version = "1.6.0" }
|
||||
espresso-core = { module = "androidx.test.espresso:espresso-core", version = "3.7.0" }
|
||||
ext-junit = { module = "androidx.test.ext:junit", version = "1.3.0" }
|
||||
firebase-analytics = { module = "com.google.firebase:firebase-analytics" }
|
||||
firebase-bom = { module = "com.google.firebase:firebase-bom", version = "34.2.0" }
|
||||
firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" }
|
||||
firebase-performance = { module = "com.google.firebase:firebase-perf" }
|
||||
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
|
||||
hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" }
|
||||
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
|
||||
hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version = "1.3.0" }
|
||||
junit = { module = "junit:junit", version = "4.13.2" }
|
||||
kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||
kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.4.0" }
|
||||
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines-android" }
|
||||
kotlinx-coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava", version.ref = "kotlinx-coroutines-android" }
|
||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.9.0" }
|
||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||
lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "lifecycle" }
|
||||
lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycle" }
|
||||
lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle" }
|
||||
lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" }
|
||||
lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
||||
lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" }
|
||||
lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
|
||||
location-services = { module = "com.google.android.gms:play-services-location", version = "21.3.0" }
|
||||
maps-compose = { module = "com.google.maps.android:maps-compose", version.ref = "maps-compose" }
|
||||
maps-compose-utils = { module = "com.google.maps.android:maps-compose-utils", version.ref = "maps-compose" }
|
||||
maps-compose-widgets = { module = "com.google.maps.android:maps-compose-widgets", version.ref = "maps-compose" }
|
||||
markdown-renderer = { module = "com.mikepenz:multiplatform-markdown-renderer", version.ref = "markdownRenderer" }
|
||||
markdown-renderer-m3 = { module = "com.mikepenz:multiplatform-markdown-renderer-m3", version.ref = "markdownRenderer" }
|
||||
markdown-renderer-android = { module = "com.mikepenz:multiplatform-markdown-renderer-android", version.ref = "markdownRenderer" }
|
||||
material = { module = "com.google.android.material:material", version = "1.13.0" }
|
||||
mgrs = { module = "mil.nga:mgrs", version = "2.1.3" }
|
||||
navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation" }
|
||||
navigation-testing = { module = "androidx.navigation:navigation-testing", version.ref = "navigation" }
|
||||
navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "navigation3" }
|
||||
navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "navigation3" }
|
||||
okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||
okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
|
||||
org-eclipse-paho-client-mqttv3 = { module = "org.eclipse.paho:org.eclipse.paho.client.mqttv3", version = "1.2.5" }
|
||||
osmbonuspack = { module = "com.github.MKergall:osmbonuspack", version = "6.9.0" }
|
||||
osmdroid-android = { module = "org.osmdroid:osmdroid-android", version.ref = "osmdroid-android" }
|
||||
osmdroid-geopackage = { module = "org.osmdroid:osmdroid-geopackage", version.ref = "osmdroid-android" }
|
||||
protobuf-kotlin = { module = "com.google.protobuf:protobuf-kotlin", version.ref = "protobuf" }
|
||||
protoc = { module = "com.google.protobuf:protoc", version.ref = "protobuf" }
|
||||
retrofit2 = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||
retrofit2-kotlin-serialization = { module = "com.squareup.retrofit2:converter-kotlinx-serialization", version.ref = "retrofit" }
|
||||
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
||||
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
room-testing = { module = "androidx.room:room-testing", version.ref = "room" }
|
||||
streamsupport-minifuture = { module = "net.sourceforge.streamsupport:streamsupport-minifuture", version = "1.7.4" }
|
||||
timber = { module = "com.jakewharton.timber:timber", version = "5.0.1" }
|
||||
usb-serial-android = { module = "com.github.mik3y:usb-serial-for-android", version = "3.9.0" }
|
||||
work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version = "2.10.4" }
|
||||
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version = "4.3.0" }
|
||||
zxing-core = { module = "com.google.zxing:core", version = "3.5.3" }
|
||||
truth = { module = "com.google.truth:truth", version = "1.4.5" }
|
||||
|
||||
# Dependencies of the included build-logic
|
||||
android-gradleApiPlugin = { module = "com.android.tools.build:gradle-api", version.ref = "agp" }
|
||||
android-tools-common = { module = "com.android.tools:common", version = "31.13.0" }
|
||||
compose-gradlePlugin = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" }
|
||||
firebase-crashlytics-gradlePlugin = { module = "com.google.firebase:firebase-crashlytics-gradle", version = "3.0.6" }
|
||||
firebase-performance-gradlePlugin = { module = "com.google.firebase:perf-plugin", version = "2.0.1" }
|
||||
ksp-gradlePlugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "devtools-ksp" }
|
||||
room-gradlePlugin = { module = "androidx.room:room-gradle-plugin", version.ref = "room" }
|
||||
spotless-gradlePlugin = { module = "com.diffplug.spotless:spotless-plugin-gradle", version = "7.2.1" }
|
||||
androidx-lint-gradle = { module = "androidx.lint:lint-gradle", version = "1.0.0-alpha05" }
|
||||
detekt-gradle = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
|
||||
detekt-compose = { module = "io.nlopez.compose.rules:detekt", version = "0.4.27" }
|
||||
detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
|
||||
|
||||
[bundles]
|
||||
# Core AndroidX
|
||||
androidx = ["core-ktx", "appcompat", "appcompat-resources", "actvity-ktx", "activity-compose"]
|
||||
|
||||
# UI
|
||||
ui = ["material", "constraintlayout", "compose-material3", "compose-material-icons-extended", "compose-ui-tooling-preview", "compose-runtime-livedata"]
|
||||
adaptive = ["adaptive", "adaptive-layout", "adaptive-navigation", "adaptive-navigation-android", "adaptive-navigation-suite"]
|
||||
ui-tooling = ["compose-ui-tooling"] #Separate for debugImplementation
|
||||
ui = ["material", "constraintlayout", "androidx-compose-material3", "androidx-compose-material-iconsExtended", "androidx-compose-ui-tooling-preview", "compose-runtime-livedata"]
|
||||
adaptive = ["androidx-compose-material3-adaptive", "androidx-compose-material3-adaptive-layout", "androidx-compose-material3-adaptive-navigation", "androidx-compose-material3-navigationSuite"]
|
||||
ui-tooling = ["androidx-compose-ui-tooling"] #Separate for debugImplementation
|
||||
markdown = ["markdown-renderer", "markdown-renderer-m3", "markdown-renderer-android"]
|
||||
|
||||
# Lifecycle
|
||||
|
|
@ -188,8 +174,8 @@ hilt = ["hilt-android", "hilt-navigation-compose"]
|
|||
|
||||
# Testing
|
||||
testing = ["junit", "ext-junit"]
|
||||
testing-android = ["espresso-core", "compose-ui-test-junit4"]
|
||||
testing-android-manifest = ["compose-ui-test-manifest"]
|
||||
testing-android = ["espresso-core", "androidx-compose-ui-test"]
|
||||
testing-android-manifest = ["androidx-compose-ui-testManifest"]
|
||||
testing-hilt = ["hilt-android-testing"]
|
||||
testing-navigation = ["navigation-testing"]
|
||||
testing-room = ["room-testing"]
|
||||
|
|
@ -201,8 +187,7 @@ osm = ["osmdroid-android", "osmbonuspack", "mgrs"]
|
|||
maps-compose = ["location-services", "maps-compose", "maps-compose-utils", "maps-compose-widgets"]
|
||||
|
||||
# Firebase
|
||||
firebase = ["firebase-analytics", "firebase-crashlytics"]
|
||||
|
||||
firebase = ["firebase-analytics", "firebase-crashlytics", "firebase-performance"]
|
||||
# Datadog
|
||||
datadog = ["dd-sdk-android-compose", "dd-sdk-android-logs", "dd-sdk-android-okhttp", "dd-sdk-android-rum", "dd-sdk-android-session-replay", "dd-sdk-android-session-replay-compose", "dd-sdk-android-timber", "dd-sdk-android-trace", "dd-sdk-android-trace-otel"]
|
||||
|
||||
|
|
@ -218,19 +203,36 @@ coil = ["coil", "coil-network-core", "coil-network-okhttp", "coil-svg"]
|
|||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||
android-lint = { id = "com.android.lint", version.ref = "agp" }
|
||||
compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
datadog = { id = "com.datadoghq.dd-sdk-android-gradle-plugin", version.ref = "dd-sdk-android-gradle-plugin" }
|
||||
datadog = { id = "com.datadoghq.dd-sdk-android-gradle-plugin", version = "1.19.0" }
|
||||
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
|
||||
dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
|
||||
dependency-analysis = { id = "com.autonomousapps.dependency-analysis", version = "3.0.2" }
|
||||
dokka = { id = "org.jetbrains.dokka", version = "2.0.0" }
|
||||
devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "devtools-ksp" }
|
||||
firebase-crashlytics = { id = "com.google.firebase.crashlytics" , version.ref = "crashlytics" }
|
||||
google-services = { id = "com.google.gms.google-services", version.ref = "google-services" }
|
||||
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version = "3.0.6" }
|
||||
firebase-perf = { id = "com.google.firebase.firebase-perf", version = "2.0.1" }
|
||||
google-services = { id = "com.google.gms.google-services", version = "4.4.3" }
|
||||
hilt = { id = "com.google.dagger.hilt.android" , version.ref = "hilt" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
|
||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
|
||||
protobuf = { id = "com.google.protobuf", version.ref = "protobuf-gradle-plugin" }
|
||||
secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets-gradle-plugin" }
|
||||
spotless = { id = "com.diffplug.spotless", version .ref= "spotless" }
|
||||
kover = { id = "org.jetbrains.kotlinx.kover", version = "0.9.1" }
|
||||
protobuf = { id = "com.google.protobuf", version = "0.9.5" }
|
||||
room = { id = "androidx.room", version.ref = "room" }
|
||||
secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version = "2.0.1" }
|
||||
spotless = { id = "com.diffplug.spotless", version = "7.2.1" }
|
||||
|
||||
# Plugins defined by this project
|
||||
meshtastic-android-application = { id = "meshtastic.android.application" }
|
||||
meshtastic-android-library = { id = "meshtastic.android.library" }
|
||||
meshtastic-android-application-flavors = { id = "meshtastic.android.application.flavors" }
|
||||
meshtastic-android-lint = { id = "meshtastic.android.lint" }
|
||||
meshtastic-android-application-compose = { id = "meshtastic.android.application.compose" }
|
||||
meshtastic-android-application-firebase = { id = "meshtastic.android.application.firebase" }
|
||||
meshtastic-android-library-compose = { id = "meshtastic.android.library.compose" }
|
||||
meshtastic-android-room = { id = "meshtastic.android.room" }
|
||||
meshtastic-android-test = { id = "meshtastic.android.test" }
|
||||
meshtastic-hilt = { id = "meshtastic.hilt" }
|
||||
meshtastic-detekt = { id = "meshtastic.detekt" }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import org.gradle.kotlin.dsl.androidTestImplementation
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
|
|
@ -16,53 +34,26 @@
|
|||
*/
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.meshtastic.android.application)
|
||||
alias(libs.plugins.meshtastic.android.application.compose)
|
||||
alias(libs.plugins.meshtastic.android.lint)
|
||||
alias(libs.plugins.kotlin.parcelize)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.compose)
|
||||
alias(libs.plugins.protobuf)
|
||||
alias(libs.plugins.detekt)
|
||||
alias(libs.plugins.spotless)
|
||||
alias(libs.plugins.kover)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.meshtastic.android.meshserviceexample"
|
||||
compileSdk = Configs.COMPILE_SDK
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.meshtastic.android.meshserviceexample"
|
||||
minSdk = 26
|
||||
targetSdk = Configs.TARGET_SDK
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
buildFeatures {
|
||||
aidl = true
|
||||
}
|
||||
buildFeatures { aidl = true }
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
||||
kotlin { jvmToolchain(21) }
|
||||
|
||||
// per protobuf-gradle-plugin docs, this is recommended for android
|
||||
protobuf {
|
||||
protoc {
|
||||
artifact = libs.protobuf.protoc.get().toString()
|
||||
}
|
||||
protoc { protoc { artifact = "com.google.protobuf:protoc:4.32.0" } }
|
||||
generateProtoTasks {
|
||||
all().forEach { task ->
|
||||
task.builtins {
|
||||
|
|
@ -82,20 +73,11 @@ dependencies {
|
|||
androidTestImplementation(libs.ext.junit)
|
||||
androidTestImplementation(libs.espresso.core)
|
||||
|
||||
implementation(libs.bundles.androidx)
|
||||
implementation(libs.bundles.protobuf)
|
||||
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
// OSM
|
||||
implementation(libs.bundles.osm)
|
||||
implementation(libs.osmdroid.geopackage) {
|
||||
exclude(group = "com.j256.ormlite")
|
||||
}
|
||||
detektPlugins(libs.detekt.formatting)
|
||||
}
|
||||
|
||||
detekt {
|
||||
config.setFrom("../config/detekt/detekt.yml")
|
||||
baseline = file("../config/detekt/detekt-baseline-meshserviceexample.xml")
|
||||
implementation(libs.osmdroid.geopackage) { exclude(group = "com.j256.ormlite") }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,63 +15,30 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.hilt)
|
||||
alias(libs.plugins.devtools.ksp)
|
||||
alias(libs.plugins.detekt)
|
||||
id("kotlinx-serialization")
|
||||
alias(libs.plugins.meshtastic.android.library)
|
||||
alias(libs.plugins.meshtastic.android.lint)
|
||||
alias(libs.plugins.meshtastic.hilt)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.spotless)
|
||||
alias(libs.plugins.dokka)
|
||||
alias(libs.plugins.kover)
|
||||
alias(libs.plugins.protobuf)
|
||||
}
|
||||
|
||||
android {
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
compileSdk = Configs.COMPILE_SDK
|
||||
defaultConfig {
|
||||
minSdk = Configs.MIN_SDK
|
||||
}
|
||||
buildFeatures { buildConfig = true }
|
||||
compileSdk = 36
|
||||
defaultConfig { minSdk = 26 }
|
||||
|
||||
namespace = "com.geeksville.mesh.network"
|
||||
|
||||
flavorDimensions += "default"
|
||||
productFlavors {
|
||||
create("fdroid") {
|
||||
dimension = "default"
|
||||
}
|
||||
create("google") {
|
||||
dimension = "default"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
||||
kotlin { jvmToolchain(21) }
|
||||
|
||||
dependencies {
|
||||
implementation(libs.bundles.hilt)
|
||||
implementation(libs.bundles.retrofit)
|
||||
implementation(libs.bundles.coil)
|
||||
"googleImplementation"(libs.bundles.datadog)
|
||||
ksp(libs.hilt.compiler)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
detektPlugins(libs.detekt.formatting)
|
||||
}
|
||||
|
||||
detekt {
|
||||
config.setFrom("../config/detekt/detekt.yml")
|
||||
baseline = file("../config/detekt/detekt-baseline-network.xml")
|
||||
source.setFrom(
|
||||
files(
|
||||
"src/main/java",
|
||||
"google/main/java",
|
||||
"fdroid/main/java",
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,10 @@ import org.gradle.kotlin.dsl.maven
|
|||
*/
|
||||
|
||||
include(":app", ":network", ":mesh_service_example")
|
||||
rootProject.name = "Meshtastic Android"
|
||||
rootProject.name = "MeshtasticAndroid"
|
||||
|
||||
pluginManagement {
|
||||
includeBuild("build-logic")
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
|
|
@ -63,4 +64,4 @@ toolchainManagement {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue