From 592743bffde0b838b4c60f0461324331b7268f18 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 6 Mar 2024 13:37:19 +0100 Subject: [PATCH 01/27] Don't update SLF4J dependency --- gradle/libs.versions.toml | 3 ++- renovate.json | 17 +++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d5881afa..621ee0e3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -40,7 +40,7 @@ publishPlugin = "1.2.1" markdown = "0.4.1" leakcanary = "2.14" mockk = "1.13.10" -slf4j = "2.0.13" # don't update +slf4j = "1.7.36" # don't update robolectric = "4.12.1" material3 = "1.3.0-beta03" material = "1.6.8" @@ -57,6 +57,7 @@ androidx-activity-compose = { group = "androidx.activity", name = "activity-comp androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidxAnnotation" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" } androidx-benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidxMacroBenchmark" } +androidx-compose-compiler = { group = "androidx.compose.compiler", name = "compiler", version.ref = "androidxComposeCompiler" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" } androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" } androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" } diff --git a/renovate.json b/renovate.json index 352aedb7..243fa0e7 100644 --- a/renovate.json +++ b/renovate.json @@ -10,21 +10,18 @@ "packageRules": [ { "matchPackagePatterns": [ - "androidx.compose.compiler:compiler" + "androidx.compose.compiler:compiler", + "org.jetbrains.kotlin.*", + "com.google.devtools.ksp", + "androidx.compose.compiler" ], "groupName": "kotlin" }, { - "matchPackagePatterns": [ - "org.jetbrains.kotlin.*" + "matchPackageNames": [ + "org.slf4j:slf4j-simple" ], - "groupName": "kotlin" - }, - { - "matchPackagePatterns": [ - "com.google.devtools.ksp" - ], - "groupName": "kotlin" + "enabled": false } ] } From e9d773923fdefc9a9a11d79bc98bf76e12b77b90 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 6 Mar 2024 13:40:23 +0100 Subject: [PATCH 02/27] Migration to Nordic Gradle Plugins 2.0 --- analytics/build.gradle.kts | 2 +- build.gradle.kts | 3 ++- core/build.gradle.kts | 2 +- gradle/libs.versions.toml | 3 ++- logger/build.gradle.kts | 2 +- navigation/build.gradle.kts | 2 +- permissions-ble/build.gradle.kts | 4 ++-- permissions-internet/build.gradle.kts | 2 +- permissions-nfc/build.gradle.kts | 2 +- theme/build.gradle.kts | 2 +- 10 files changed, 13 insertions(+), 11 deletions(-) diff --git a/analytics/build.gradle.kts b/analytics/build.gradle.kts index f02363a2..42867402 100644 --- a/analytics/build.gradle.kts +++ b/analytics/build.gradle.kts @@ -32,7 +32,7 @@ plugins { alias(libs.plugins.nordic.library.compose) alias(libs.plugins.nordic.hilt) - alias(libs.plugins.nordic.nexus) + alias(libs.plugins.nordic.nexus.android) } group = "no.nordicsemi.android.common" diff --git a/build.gradle.kts b/build.gradle.kts index d32b853a..66e561e9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,5 +41,6 @@ plugins { alias(libs.plugins.nordic.library) apply false alias(libs.plugins.nordic.feature) apply false alias(libs.plugins.nordic.hilt) apply false - alias(libs.plugins.nordic.nexus) apply false + alias(libs.plugins.nordic.nexus.android) apply false + alias(libs.plugins.nordic.nexus.kotlin) apply false } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 57987dfe..71aae484 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -31,7 +31,7 @@ plugins { alias(libs.plugins.nordic.library.compose) - alias(libs.plugins.nordic.nexus) + alias(libs.plugins.nordic.nexus.android) alias(libs.plugins.kotlin.parcelize) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 621ee0e3..e3c86c99 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -144,7 +144,8 @@ nordic-library-compose = { id = "no.nordicsemi.android.gradle.library.compose", nordic-feature = { id = "no.nordicsemi.android.gradle.feature", version.ref = "nordicPlugins" } nordic-hilt = { id = "no.nordicsemi.android.gradle.hilt", version.ref = "nordicPlugins" } nordic-kotlin = { id = "no.nordicsemi.android.gradle.kotlin", version.ref = "nordicPlugins" } -nordic-nexus = { id = "no.nordicsemi.android.gradle.nexus", version.ref = "nordicPlugins" } +nordic-nexus-android = { id = "no.nordicsemi.android.gradle.nexus", version.ref = "nordicPlugins" } +nordic-nexus-kotlin = { id = "no.nordicsemi.kotlin.gradle.nexus", version.ref = "nordicPlugins" } android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" } diff --git a/logger/build.gradle.kts b/logger/build.gradle.kts index c7e2d89a..fdcb8b4a 100644 --- a/logger/build.gradle.kts +++ b/logger/build.gradle.kts @@ -32,7 +32,7 @@ plugins { alias(libs.plugins.nordic.library.compose) alias(libs.plugins.nordic.kotlin) - alias(libs.plugins.nordic.nexus) + alias(libs.plugins.nordic.nexus.android) } group = "no.nordicsemi.android.common" diff --git a/navigation/build.gradle.kts b/navigation/build.gradle.kts index 40a50913..aaf09838 100644 --- a/navigation/build.gradle.kts +++ b/navigation/build.gradle.kts @@ -32,7 +32,7 @@ plugins { alias(libs.plugins.nordic.library.compose) alias(libs.plugins.nordic.hilt) - alias(libs.plugins.nordic.nexus) + alias(libs.plugins.nordic.nexus.android) alias(libs.plugins.kotlin.parcelize) } diff --git a/permissions-ble/build.gradle.kts b/permissions-ble/build.gradle.kts index 5c18c8dc..cf11e36b 100644 --- a/permissions-ble/build.gradle.kts +++ b/permissions-ble/build.gradle.kts @@ -32,14 +32,14 @@ plugins { alias(libs.plugins.nordic.library.compose) alias(libs.plugins.nordic.hilt) - alias(libs.plugins.nordic.nexus) + alias(libs.plugins.nordic.nexus.android) } group = "no.nordicsemi.android.common" nordicNexusPublishing { POM_ARTIFACT_ID = "permissions-ble" - POM_NAME = "Nordic library for checking required ble permissions." + POM_NAME = "Nordic library for checking Bluetooth LE and Location permissions." POM_DESCRIPTION = "Nordic Android Common Libraries" POM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" diff --git a/permissions-internet/build.gradle.kts b/permissions-internet/build.gradle.kts index 47687405..8b036ee3 100644 --- a/permissions-internet/build.gradle.kts +++ b/permissions-internet/build.gradle.kts @@ -32,7 +32,7 @@ plugins { alias(libs.plugins.nordic.library.compose) alias(libs.plugins.nordic.hilt) - alias(libs.plugins.nordic.nexus) + alias(libs.plugins.nordic.nexus.android) } group = "no.nordicsemi.android.common" diff --git a/permissions-nfc/build.gradle.kts b/permissions-nfc/build.gradle.kts index 205727f4..f3d3bf9f 100644 --- a/permissions-nfc/build.gradle.kts +++ b/permissions-nfc/build.gradle.kts @@ -32,7 +32,7 @@ plugins { alias(libs.plugins.nordic.library.compose) alias(libs.plugins.nordic.hilt) - alias(libs.plugins.nordic.nexus) + alias(libs.plugins.nordic.nexus.android) } group = "no.nordicsemi.android.common" diff --git a/theme/build.gradle.kts b/theme/build.gradle.kts index 43111850..3a6f7c8e 100644 --- a/theme/build.gradle.kts +++ b/theme/build.gradle.kts @@ -32,7 +32,7 @@ plugins { alias(libs.plugins.nordic.library.compose) alias(libs.plugins.nordic.hilt) - alias(libs.plugins.nordic.nexus) + alias(libs.plugins.nordic.nexus.android) } group = "no.nordicsemi.android.common" From 24ff17e652553ecbc9d3b2c6499b27c9ecd0a753 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 6 Mar 2024 13:41:53 +0100 Subject: [PATCH 03/27] Changing to default owner for libraries (MAG) --- analytics/build.gradle.kts | 4 ---- core/build.gradle.kts | 4 ---- logger/build.gradle.kts | 4 ---- navigation/build.gradle.kts | 4 ---- permissions-ble/build.gradle.kts | 4 ---- permissions-internet/build.gradle.kts | 4 ---- permissions-nfc/build.gradle.kts | 4 ---- theme/build.gradle.kts | 6 +----- 8 files changed, 1 insertion(+), 33 deletions(-) diff --git a/analytics/build.gradle.kts b/analytics/build.gradle.kts index 42867402..cb30cc24 100644 --- a/analytics/build.gradle.kts +++ b/analytics/build.gradle.kts @@ -46,10 +46,6 @@ nordicNexusPublishing { POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" - - POM_DEVELOPER_ID = "syzi" - POM_DEVELOPER_NAME = "Sylwester Zieliński" - POM_DEVELOPER_EMAIL = "sylwester.zielinski@nordicsemi.no" } android { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 71aae484..5cf1cc7b 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -46,10 +46,6 @@ nordicNexusPublishing { POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" - - POM_DEVELOPER_ID = "syzi" - POM_DEVELOPER_NAME = "Sylwester Zieliński" - POM_DEVELOPER_EMAIL = "sylwester.zielinski@nordicsemi.no" } android { diff --git a/logger/build.gradle.kts b/logger/build.gradle.kts index fdcb8b4a..0cac9765 100644 --- a/logger/build.gradle.kts +++ b/logger/build.gradle.kts @@ -50,10 +50,6 @@ nordicNexusPublishing { POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" - - POM_DEVELOPER_ID = "syzi" - POM_DEVELOPER_NAME = "Sylwester Zieliński" - POM_DEVELOPER_EMAIL = "sylwester.zielinski@nordicsemi.no" } dependencies { diff --git a/navigation/build.gradle.kts b/navigation/build.gradle.kts index aaf09838..b43f4998 100644 --- a/navigation/build.gradle.kts +++ b/navigation/build.gradle.kts @@ -47,10 +47,6 @@ nordicNexusPublishing { POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" - - POM_DEVELOPER_ID = "syzi" - POM_DEVELOPER_NAME = "Sylwester Zieliński" - POM_DEVELOPER_EMAIL = "sylwester.zielinski@nordicsemi.no" } android { diff --git a/permissions-ble/build.gradle.kts b/permissions-ble/build.gradle.kts index cf11e36b..3b22414a 100644 --- a/permissions-ble/build.gradle.kts +++ b/permissions-ble/build.gradle.kts @@ -46,10 +46,6 @@ nordicNexusPublishing { POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" - - POM_DEVELOPER_ID = "syzi" - POM_DEVELOPER_NAME = "Sylwester Zieliński" - POM_DEVELOPER_EMAIL = "sylwester.zielinski@nordicsemi.no" } android { diff --git a/permissions-internet/build.gradle.kts b/permissions-internet/build.gradle.kts index 8b036ee3..70b39ab5 100644 --- a/permissions-internet/build.gradle.kts +++ b/permissions-internet/build.gradle.kts @@ -46,10 +46,6 @@ nordicNexusPublishing { POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" - - POM_DEVELOPER_ID = "syzi" - POM_DEVELOPER_NAME = "Sylwester Zieliński" - POM_DEVELOPER_EMAIL = "sylwester.zielinski@nordicsemi.no" } android { diff --git a/permissions-nfc/build.gradle.kts b/permissions-nfc/build.gradle.kts index f3d3bf9f..83f1c0a1 100644 --- a/permissions-nfc/build.gradle.kts +++ b/permissions-nfc/build.gradle.kts @@ -46,10 +46,6 @@ nordicNexusPublishing { POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" - - POM_DEVELOPER_ID = "hiar" - POM_DEVELOPER_NAME = "Himali Aryal" - POM_DEVELOPER_EMAIL = "himali.aryal@nordicsemi.no" } android { diff --git a/theme/build.gradle.kts b/theme/build.gradle.kts index 3a6f7c8e..7a56f49c 100644 --- a/theme/build.gradle.kts +++ b/theme/build.gradle.kts @@ -39,17 +39,13 @@ group = "no.nordicsemi.android.common" nordicNexusPublishing { POM_ARTIFACT_ID = "theme" - POM_NAME = "Nordic theme library for Android" + POM_NAME = "Nordic theme library for Android." POM_DESCRIPTION = "Nordic Android Common Libraries" POM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" - - POM_DEVELOPER_ID = "syzi" - POM_DEVELOPER_NAME = "Sylwester Zieliński" - POM_DEVELOPER_EMAIL = "sylwester.zielinski@nordicsemi.no" } android { From 4d6b8b7b65897b2878157b2487685ba4e23d4297 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 6 Mar 2024 13:43:32 +0100 Subject: [PATCH 04/27] Adding build.gradle.kts for :data module --- data/build.gradle.kts | 48 +++++++++++++++++++++++++++++++++++++++++++ data/module-rules.pro | 17 +++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 data/build.gradle.kts create mode 100644 data/module-rules.pro diff --git a/data/build.gradle.kts b/data/build.gradle.kts new file mode 100644 index 00000000..c1f2a66f --- /dev/null +++ b/data/build.gradle.kts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +plugins { + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.nordic.nexus.kotlin) +} + +group = "no.nordicsemi.kotlin" + +nordicNexusPublishing { + POM_ARTIFACT_ID = "data" + POM_NAME = "Nordic library for analytics." + + POM_DESCRIPTION = "Nordic Android Common Libraries" + POM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" + POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" + POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" + POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" +} \ No newline at end of file diff --git a/data/module-rules.pro b/data/module-rules.pro new file mode 100644 index 00000000..7e9d74c9 --- /dev/null +++ b/data/module-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:/Users/alno/AppData/Local/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} \ No newline at end of file From 38581f0cef01ca1ba9219a0b467cc4a342159d49 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 6 Mar 2024 13:43:58 +0100 Subject: [PATCH 05/27] Modules descriptions improved --- permissions-internet/build.gradle.kts | 2 +- permissions-nfc/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/permissions-internet/build.gradle.kts b/permissions-internet/build.gradle.kts index 70b39ab5..9e13362a 100644 --- a/permissions-internet/build.gradle.kts +++ b/permissions-internet/build.gradle.kts @@ -39,7 +39,7 @@ group = "no.nordicsemi.android.common" nordicNexusPublishing { POM_ARTIFACT_ID = "permissions-internet" - POM_NAME = "Nordic library for checking required internet permissions." + POM_NAME = "Nordic library for checking Internet permission." POM_DESCRIPTION = "Nordic Android Common Libraries" POM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" diff --git a/permissions-nfc/build.gradle.kts b/permissions-nfc/build.gradle.kts index 83f1c0a1..5ad6a5eb 100644 --- a/permissions-nfc/build.gradle.kts +++ b/permissions-nfc/build.gradle.kts @@ -39,7 +39,7 @@ group = "no.nordicsemi.android.common" nordicNexusPublishing { POM_ARTIFACT_ID = "permissions-nfc" - POM_NAME = "Nordic library for checking required nfc permission." + POM_NAME = "Nordic library for checking NFC permission." POM_DESCRIPTION = "Nordic Android Common Libraries" POM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" From c7f31da853c23366133763cd63cc787643a99464 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Thu, 28 Mar 2024 14:57:00 +0100 Subject: [PATCH 06/27] :data module removed from Commons --- data/.gitignore | 1 - data/build.gradle.kts | 48 ---- data/module-rules.pro | 17 -- .../kotlin/data/bitwiseOperations.kt | 268 ------------------ .../kotlin/data/byteArrayOperations.kt | 145 ---------- .../no/nordicsemi/kotlin/data/hexEncoding.kt | 52 ---- .../nordicsemi/kotlin/data/uuidOperations.kt | 92 ------ .../kotlin/data/BitwiseOperationsKtTest.kt | 168 ----------- .../kotlin/data/ByteArrayOperationsTest.kt | 108 ------- .../kotlin/data/UuidOperationsKtTest.kt | 91 ------ 10 files changed, 990 deletions(-) delete mode 100644 data/.gitignore delete mode 100644 data/build.gradle.kts delete mode 100644 data/module-rules.pro delete mode 100644 data/src/main/java/no/nordicsemi/kotlin/data/bitwiseOperations.kt delete mode 100644 data/src/main/java/no/nordicsemi/kotlin/data/byteArrayOperations.kt delete mode 100644 data/src/main/java/no/nordicsemi/kotlin/data/hexEncoding.kt delete mode 100644 data/src/main/java/no/nordicsemi/kotlin/data/uuidOperations.kt delete mode 100644 data/src/test/java/no/nordicsemi/kotlin/data/BitwiseOperationsKtTest.kt delete mode 100644 data/src/test/java/no/nordicsemi/kotlin/data/ByteArrayOperationsTest.kt delete mode 100644 data/src/test/java/no/nordicsemi/kotlin/data/UuidOperationsKtTest.kt diff --git a/data/.gitignore b/data/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/data/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/data/build.gradle.kts b/data/build.gradle.kts deleted file mode 100644 index c1f2a66f..00000000 --- a/data/build.gradle.kts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2023, Nordic Semiconductor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -plugins { - alias(libs.plugins.kotlin.jvm) - alias(libs.plugins.nordic.nexus.kotlin) -} - -group = "no.nordicsemi.kotlin" - -nordicNexusPublishing { - POM_ARTIFACT_ID = "data" - POM_NAME = "Nordic library for analytics." - - POM_DESCRIPTION = "Nordic Android Common Libraries" - POM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" - POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" - POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" - POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" -} \ No newline at end of file diff --git a/data/module-rules.pro b/data/module-rules.pro deleted file mode 100644 index 7e9d74c9..00000000 --- a/data/module-rules.pro +++ /dev/null @@ -1,17 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in C:/Users/alno/AppData/Local/Android/sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle.kts. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} \ No newline at end of file diff --git a/data/src/main/java/no/nordicsemi/kotlin/data/bitwiseOperations.kt b/data/src/main/java/no/nordicsemi/kotlin/data/bitwiseOperations.kt deleted file mode 100644 index 74214ed1..00000000 --- a/data/src/main/java/no/nordicsemi/kotlin/data/bitwiseOperations.kt +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (c) 2023, Nordic Semiconductor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -@file:Suppress("unused") - -package no.nordicsemi.kotlin.data - -import kotlin.experimental.and -import kotlin.experimental.xor - -/** - * Returns whether ALL bits that are equal to 1 in the given [mask] are also set to 1 - * in the receiver. - * - * Example: - * - 0b00001111 hasAllBitsSet 0b00000111 == true - * - 0b00001111 hasAllBitsSet 0b00001000 == true - * - 0b10101010 hasAllBitsSet 0b00000100 == false - * - 0b10101000 hasAllBitsSet 0b10101010 == false - * - 0b10101010 hasAllBitsSet 0b10000000 == true - * @receiver Byte value. - */ -infix fun Byte.hasAllBitsSet(mask: Int): Boolean = this and mask.toByte() == mask.toByte() - -/** - * Returns whether ALL bits that are equal to 1 in the given [mask] are set to 0 - * in the receiver. - * - * Example: - * - 0b00001111 hasAllBitsCleared 0b00000111 == false - * - 0b00001111 hasAllBitsCleared 0b00001000 == false - * - 0b10101010 hasAllBitsCleared 0b00000100 == true - * - 0b10101000 hasAllBitsCleared 0b10101010 == false - * - 0b10101010 hasAllBitsCleared 0b10000000 == false - * @receiver Byte value. - */ -infix fun Byte.hasAllBitsCleared(mask: Int): Boolean = this and mask.toByte() == 0.toByte() - -/** - * Returns whether ALL bits that are equal to 1 in the given [mask] are also set to 1 - * in the receiver. - * - * Example: - * - 0b00001111 hasAllBitsSet 0b00000111 == true - * - 0b00001111 hasAllBitsSet 0b00001000 == true - * - 0b10101010 hasAllBitsSet 0b00000100 == false - * - 0b10101000 hasAllBitsSet 0b10101010 == false - * - 0b10101010 hasAllBitsSet 0b10000000 == true - * @receiver Byte value. - */ -infix fun UByte.hasAllBitsSet(mask: Int): Boolean = this.toByte().hasAllBitsSet(mask) - -/** - * Returns whether ALL bits that are equal to 1 in the given [mask] are set to 0 - * in the receiver. - * - * Example: - * - 0b00001111 hasAllBitsCleared 0b00000111 == false - * - 0b00001111 hasAllBitsCleared 0b00001000 == false - * - 0b10101010 hasAllBitsCleared 0b00000100 == true - * - 0b10101000 hasAllBitsCleared 0b10101010 == false - * - 0b10101010 hasAllBitsCleared 0b10000000 == false - * @receiver Byte value. - */ -infix fun UByte.hasAllBitsCleared(mask: Int): Boolean = this.toByte().hasAllBitsCleared(mask) - -// ------------------------------------------------------------------------------------------------- - -/** - * Returns whether the bit at the given [bit] is set to 1 in the receiver. - * @receiver Byte value. - */ -infix fun Byte.hasBitSet(bit: Int): Boolean = this and (1 shl bit).toByte() != 0.toByte() - -/** - * Returns whether the bit at the given [bit] is set to 0 in the receiver. - * @receiver Byte value. - */ -infix fun Byte.hasBitCleared(bit: Int): Boolean = this and (1 shl bit).toByte() == 0.toByte() - -/** - * Returns whether the bit at the given [bit] is set to 1 in the receiver. - * @receiver UByte value. - */ -infix fun UByte.hasBitSet(bit: Int): Boolean = this.toByte().hasBitSet(bit) - -/** - * Returns whether the bit at the given [bit] is set to 0 in the receiver. - * @receiver UByte value. - */ -infix fun UByte.hasBitCleared(bit: Int): Boolean = this.toByte().hasBitCleared(bit) - -/** - * Returns whether the bit at the given [bit] is set to 1 in the receiver. - * @receiver Short value. - */ -infix fun Short.hasBitSet(bit: Int): Boolean = this and (1 shl bit).toShort() != 0.toShort() - -/** - * Returns whether the bit at the given [bit] is set to 0 in the receiver. - * @receiver Short value. - */ -infix fun Short.hasBitCleared(bit: Int): Boolean = this and (1 shl bit).toShort() == 0.toShort() - -/** - * Returns whether the bit at the given [bit] is set to 1 in the receiver. - * @receiver UShort value. - */ -infix fun UShort.hasBitSet(bit: Int): Boolean = this and (1 shl bit).toUShort() != 0.toUShort() - -/** - * Returns whether the bit at the given [bit] is set to 0 in the receiver. - * @receiver UShort value. - */ -infix fun UShort.hasBitCleared(bit: Int): Boolean = this and (1 shl bit).toUShort() == 0.toUShort() - -/** - * Returns whether the bit at the given [bit] is set to 1 in the receiver. - * @receiver Int value. - */ -infix fun Int.hasBitSet(bit: Int): Boolean = this and (1 shl bit) != 0 - -/** - * Returns whether the bit at the given [bit] is set to 0 in the receiver. - * @receiver Int value. - */ -infix fun Int.hasBitCleared(bit: Int): Boolean = this and (1 shl bit) == 0 - -/** - * Returns whether the bit at the given [bit] is set to 1 in the receiver. - * @receiver UInt value. - */ -infix fun UInt.hasBitSet(bit: Int): Boolean = this and (1u shl bit) != 0u - -/** - * Returns whether the bit at the given [bit] is set to 0 in the receiver. - * @receiver UInt value. - */ -infix fun UInt.hasBitCleared(bit: Int): Boolean = this and (1u shl bit) == 0u - -// ------------------------------------------------------------------------------------------------- - -/** - * Applies the XOR operator on two byte arrays. Compared to already existent - * xor functions, this one does not require the arrays to be of the same length. - * - * @param other The other byte array which is xor ed with this one. - * @return XOR of the two byte arrays. - */ -infix fun ByteArray.xor(other: ByteArray): ByteArray { - val result = ByteArray(this.count()) - for (i in this.indices) { - result[i] = (this[i] xor other[i % other.count()]) - } - return result -} - -/** - * Shifts this value left by the [bitCount] number of bits. - * - * Note that only the three lowest-order bits of the [bitCount] are used as the shift distance. - * The shift distance actually used is therefore always in the range `0..7`. - */ -infix fun Byte.shl(bitCount: Int): Byte = (this.toInt() shl bitCount).toByte() - -/** - * Shifts this value right by the [bitCount] number of bits, filling the leftmost bits with - * copies of the sign bit. - * - * Note that only the three lowest-order bits of the [bitCount] are used as the shift distance. - * The shift distance actually used is therefore always in the range `0..7`. - */ -infix fun Byte.shr(bitCount: Int): Byte = (this.toInt() shr bitCount).toByte() - -/** - * Shifts this value right by the [bitCount] number of bits, filling the leftmost bits with zeros. - * - * Note that only the three lowest-order bits of the [bitCount] are used as the shift distance. - * The shift distance actually used is therefore always in the range `0..7`. - */ -infix fun Byte.ushr(bitCount: Int): Byte = ((this.toInt() and 0xFF) ushr bitCount).toByte() - -/** - * Shifts this value left by the [bitCount] number of bits. - * - * Note that only the three lowest-order bits of the [bitCount] are used as the shift distance. - * The shift distance actually used is therefore always in the range `0..7`. - */ -infix fun UByte.shl(bitCount: Int): UByte = (this.toUInt() shl bitCount).toUByte() - -/** - * Shifts this value right by the [bitCount] number of bits, filling the leftmost bits with zeros. - * - * Note that only the three lowest-order bits of the [bitCount] are used as the shift distance. - * The shift distance actually used is therefore always in the range `0..7`. - */ -infix fun UByte.shr(bitCount: Int): UByte = (this.toUInt() shr bitCount).toUByte() - -/** - * Shifts this value left by the [bitCount] number of bits. - * - * Note that only the four lowest-order bits of the [bitCount] are used as the shift distance. - * The shift distance actually used is therefore always in the range `0..15`. - */ -infix fun Short.shl(bitCount: Int): Short = (this.toInt() shl bitCount).toShort() - -/** - * Shifts this value right by the [bitCount] number of bits, filling the leftmost bits with - * copies of the sign bit. - * - * Note that only the four lowest-order bits of the [bitCount] are used as the shift distance. - * The shift distance actually used is therefore always in the range `0..15`. - */ -infix fun Short.shr(bitCount: Int): Short = (this.toInt() ushr bitCount).toShort() - -/** - * Shifts this value right by the [bitCount] number of bits, filling the leftmost bits with - * copies of the sign bit. - * - * Note that only the four lowest-order bits of the [bitCount] are used as the shift distance. - * The shift distance actually used is therefore always in the range `0..15`. - */ -infix fun Short.ushr(bitCount: Int): Short = ((this.toInt() and 0xFFFF) ushr bitCount).toShort() - -/** - * Shifts this value left by the [bitCount] number of bits. - * - * Note that only the four lowest-order bits of the [bitCount] are used as the shift distance. - * The shift distance actually used is therefore always in the range `0..15`. - */ -infix fun UShort.shl(bitCount: Int): UShort = (this.toInt() shl bitCount).toUShort() - -/** - * Shifts this value right by the [bitCount] number of bits, filling the leftmost bits with - * copies of the sign bit. - * - * Note that only the four lowest-order bits of the [bitCount] are used as the shift distance. - * The shift distance actually used is therefore always in the range `0..15`. - */ -infix fun UShort.shr(bitCount: Int): UShort = (this.toInt() ushr bitCount).toUShort() diff --git a/data/src/main/java/no/nordicsemi/kotlin/data/byteArrayOperations.kt b/data/src/main/java/no/nordicsemi/kotlin/data/byteArrayOperations.kt deleted file mode 100644 index b3a67c24..00000000 --- a/data/src/main/java/no/nordicsemi/kotlin/data/byteArrayOperations.kt +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2023, Nordic Semiconductor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -@file:Suppress("unused") - -package no.nordicsemi.kotlin.data - -import java.nio.ByteBuffer -import java.nio.ByteOrder - -/** - * Converts an Int to a byte array using the given endianness. - * @param order The byte order, default is [ByteOrder.BIG_ENDIAN]. - */ -fun Int.toByteArray(order: ByteOrder = ByteOrder.BIG_ENDIAN) = when (order) { - ByteOrder.BIG_ENDIAN -> ByteArray(4) { (this ushr (24 - it * 8)).toByte() } - else -> ByteArray(4) { (this ushr (it * 8)).toByte() } -} - -/** - * Converts an UInt to a byte array using the given endianness. - * @param order The byte order, default is [ByteOrder.BIG_ENDIAN]. - */ -fun UInt.toByteArray(order: ByteOrder = ByteOrder.BIG_ENDIAN) = when (order) { - ByteOrder.BIG_ENDIAN -> ByteArray(4) { (this shr (24 - it * 8)).toByte() } - else -> ByteArray(4) { (this shr (it * 8)).toByte() } -} - -/** - * Converts a Short to a byte array using the given endianness. - * @param order The byte order, default is [ByteOrder.BIG_ENDIAN]. - */ -fun Short.toByteArray(order: ByteOrder = ByteOrder.BIG_ENDIAN) = when (order) { - ByteOrder.BIG_ENDIAN -> ByteArray(2) { (this ushr (8 - it * 8)).toByte() } - else -> ByteArray(2) { (this ushr (it * 8)).toByte() } -} - -/** - * Converts a UShort to a byte array using the given endianness. - * @param order The byte order, default is [ByteOrder.BIG_ENDIAN]. - */ -fun UShort.toByteArray(order: ByteOrder = ByteOrder.BIG_ENDIAN) = when (order) { - ByteOrder.BIG_ENDIAN -> ByteArray(2) { (this shr (8 - it * 8)).toByte() } - else -> ByteArray(2) { (this shr (it * 8)).toByte() } -} - -/** - * Converts a Byte to a byte array. - */ -fun Byte.toByteArray() = ByteArray(1) { this } - -/** - * Converts a UByte to a byte array. - */ -fun UByte.toByteArray() = ByteArray(1) { this.toByte() } - -//-------------------------------------------------------------------------------------------------- - -/** - * Returns an Int from a byte array with a given offset. - * - * @param offset The index to start from. - * @param order The byte order, default is [ByteOrder.BIG_ENDIAN]. - * @return Int. - * @throws IllegalArgumentException If the length of byte array is not >= offset + 4. - */ -fun ByteArray.getInt(offset: Int, order: ByteOrder = ByteOrder.BIG_ENDIAN): Int { - require(offset >= 0 && size >= offset + 4) { - throw IndexOutOfBoundsException("Cannot return an Int from an array of size $size from offset $offset") - } - return ByteBuffer.wrap(this, offset, 4).order(order).int -} - -/** - * Returns an Int from a byte array with a given offset. - * - * @param offset The index to start from. - * @param order The byte order, default is [ByteOrder.BIG_ENDIAN]. - * @return UInt. - * @throws IllegalArgumentException If the length of byte array is not >= offset + 4. - */ -fun ByteArray.getUInt(offset: Int, order: ByteOrder = ByteOrder.BIG_ENDIAN): UInt { - require(offset >= 0 && size >= offset + 4) { - throw IndexOutOfBoundsException("Cannot return an UInt from an array of size $size from offset $offset") - } - return ByteBuffer.wrap(this, offset, 4).order(order).int.toUInt() -} - -/** - * Returns an Int from a byte array with a given offset. - * - * @param offset The index to start from. - * @param order The byte order, default is [ByteOrder.BIG_ENDIAN]. - * @return Short. - * @throws IndexOutOfBoundsException If the length of the byte array is not >= offset + 2. - */ -fun ByteArray.getShort(offset: Int, order: ByteOrder = ByteOrder.BIG_ENDIAN): Short { - require(offset >= 0 && size >= offset + 2) { - throw IndexOutOfBoundsException("Cannot return a Short from an array of size $size from offset $offset") - } - return ByteBuffer.wrap(this, offset, 2).order(order).short -} - -/** - * Returns an Int from a byte array with a given offset. - * - * @param offset The index to start from. - * @param order The byte order, default is [ByteOrder.BIG_ENDIAN]. - * @return UShort. - * @throws IndexOutOfBoundsException If the length of the byte array is not >= offset + 2. - */ -fun ByteArray.getUShort(offset: Int, order: ByteOrder = ByteOrder.BIG_ENDIAN): UShort { - require(offset >= 0 && size >= offset + 2) { - throw IndexOutOfBoundsException("Cannot return a UShort from an array of size $size from offset $offset") - } - return ByteBuffer.wrap(this, offset, 2).order(order).short.toUShort() -} \ No newline at end of file diff --git a/data/src/main/java/no/nordicsemi/kotlin/data/hexEncoding.kt b/data/src/main/java/no/nordicsemi/kotlin/data/hexEncoding.kt deleted file mode 100644 index 2a990bfa..00000000 --- a/data/src/main/java/no/nordicsemi/kotlin/data/hexEncoding.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2023, Nordic Semiconductor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -@file:Suppress("unused") - -package no.nordicsemi.kotlin.data - -/** - * Converts a byte array to a hex string. - * - * @param prefixOx Whether to prefix the hex string with 0x. - * @param format The format of the hex string. - * @return Hex string representation of the byte array. - */ -@OptIn(ExperimentalStdlibApi::class) -fun ByteArray.toHexString(prefixOx: Boolean = false, format: HexFormat = HexFormat.UpperCase) = - (if (prefixOx) "0x" else "") + toHexString(format) - -/** - * Converts a byte array to a hex string. - * @param format The format of the hex string. - */ -@OptIn(ExperimentalStdlibApi::class) -fun String.decodeHex(format: HexFormat = HexFormat.UpperCase) = hexToByteArray(format) \ No newline at end of file diff --git a/data/src/main/java/no/nordicsemi/kotlin/data/uuidOperations.kt b/data/src/main/java/no/nordicsemi/kotlin/data/uuidOperations.kt deleted file mode 100644 index 510272f5..00000000 --- a/data/src/main/java/no/nordicsemi/kotlin/data/uuidOperations.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2023, Nordic Semiconductor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -@file:Suppress("unused") - -package no.nordicsemi.kotlin.data - -import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.util.UUID - -/** - * Converts a 128-bit UUID to a byte array. - * @param order The byte order, default is [ByteOrder.BIG_ENDIAN]. - */ -// TODO: 2021-08-26: Add support for 16-bit and 32-bit UUIDs. -fun UUID.toByteArray(shortenIfPossible: Boolean, order: ByteOrder = ByteOrder.BIG_ENDIAN): ByteArray = - ByteBuffer.wrap(ByteArray(16)) - .order(order) - .apply { - if (order == ByteOrder.BIG_ENDIAN) { - putLong(mostSignificantBits) - putLong(leastSignificantBits) - } else { - putLong(leastSignificantBits) - putLong(mostSignificantBits) - } - } - .array() - -/** - * Converts a byte array to a 128-bit UUID. - * @param offset The index to start from. - * @param order The byte order, default is [ByteOrder.BIG_ENDIAN]. - * @return UUID - * @throws IllegalArgumentException If the byte array is shorter than 16 bytes long. - */ -fun ByteArray.getUuid(offset: Int, order: ByteOrder = ByteOrder.BIG_ENDIAN): UUID { - require(offset >= 0 && size >= offset + 16) { - throw IndexOutOfBoundsException("Cannot return a UUID from an array of size $size from offset $offset") - } - val buffer = ByteBuffer.wrap(this).order(order).position(offset) - return when (order) { - ByteOrder.BIG_ENDIAN -> UUID(buffer.long, buffer.long) - else -> { - val leastSignificantBits = buffer.long - val mostSignificantBits = buffer.long - UUID(mostSignificantBits, leastSignificantBits) - } - } -} - -/** - * Converts a byte array to a 128-bit UUID. - * @param order The byte order, default is [ByteOrder.BIG_ENDIAN]. - * @return UUID - * @throws IllegalArgumentException If the byte array is not 16 bytes long. - */ -fun ByteArray.toUuid(order: ByteOrder = ByteOrder.BIG_ENDIAN): UUID { - require(size == 16) { - throw IndexOutOfBoundsException("Array is $size bytes long, expected 16 bytes") - } - return getUuid(0, order) -} \ No newline at end of file diff --git a/data/src/test/java/no/nordicsemi/kotlin/data/BitwiseOperationsKtTest.kt b/data/src/test/java/no/nordicsemi/kotlin/data/BitwiseOperationsKtTest.kt deleted file mode 100644 index 1e340f89..00000000 --- a/data/src/test/java/no/nordicsemi/kotlin/data/BitwiseOperationsKtTest.kt +++ /dev/null @@ -1,168 +0,0 @@ -package no.nordicsemi.kotlin.data - -import kotlin.test.Test -import kotlin.test.assertContentEquals -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -internal class BitwiseOperationsKtTest { - - @Test - fun hasAllBitsSet() { - val value = 0b1010_1001.toByte() - assertTrue(value.hasAllBitsSet(0b1000_0001)) - assertTrue(value.hasAllBitsSet(0b1010_1001)) - assertTrue(value.hasAllBitsSet(0b0000_0001)) - assertTrue(value.hasAllBitsSet(0b0010_1000)) - assertTrue(value.hasAllBitsSet(0b0000_0000)) - assertFalse(value.hasAllBitsSet(0b1100_0001)) - assertFalse(value.hasAllBitsSet(0b1000_0010)) - assertFalse(value.hasAllBitsSet(0b1010_0101)) - assertFalse(value.hasAllBitsSet(0b1010_1111)) - assertFalse(value.hasAllBitsSet(0b0000_1100)) - } - - @Test - fun hasAllBitsCleared() { - val value = 0b1010_1001.toByte() - assertFalse(value.hasAllBitsCleared(0b1111_1111)) - assertFalse(value.hasAllBitsCleared(0b1000_0001)) - assertFalse(value.hasAllBitsCleared(0b1010_1001)) - assertFalse(value.hasAllBitsCleared(0b0000_0001)) - assertFalse(value.hasAllBitsCleared(0b0010_1000)) - assertTrue(value.hasAllBitsCleared(0b0000_0000)) - assertTrue(value.hasAllBitsCleared(0b0100_0010)) - assertTrue(value.hasAllBitsCleared(0b0101_0110)) - assertTrue(value.hasAllBitsCleared(0b0000_0100)) - assertTrue(value.hasAllBitsCleared(0b0001_0000)) - } - - @Test - fun bitTests() { - val value = 0b1010_1001.toByte() - assertTrue(value.hasBitSet(0)) - assertFalse(value.hasBitCleared(0)) - assertTrue(value.hasBitCleared(1)) - assertFalse(value.hasBitSet(1)) - assertTrue(value.hasBitCleared(2)) - assertFalse(value.hasBitSet(2)) - assertTrue(value.hasBitSet(3)) - assertFalse(value.hasBitCleared(3)) - assertTrue(value.hasBitCleared(4)) - assertFalse(value.hasBitSet(4)) - assertTrue(value.hasBitSet(5)) - assertFalse(value.hasBitCleared(5)) - assertTrue(value.hasBitCleared(6)) - assertFalse(value.hasBitSet(6)) - assertTrue(value.hasBitSet(7)) - assertFalse(value.hasBitCleared(7)) - } - - @Test - fun bitTestsInt() { - val value = 0x2345_6789 - assertTrue(value.hasBitSet(0)) - assertFalse(value.hasBitCleared(0)) - assertTrue(value.hasBitCleared(31)) - assertFalse(value.hasBitSet(31)) - } - - @Test - fun xor() { - val left = byteArrayOf(0xF1.toByte(), 0x02, 0x03, 0x04) - val right = byteArrayOf(0x25, 0x06, 0x07, 0x08) - val xor = left xor right - assertContentEquals(byteArrayOf(0xD4.toByte(), 0x04, 0x04, 0x0C), xor) - } - - @Test - fun byteShl() { - val value = 0b1010_1001.toByte() - assertEquals(0b0101_0010.toByte(), value shl 1) - assertEquals(0b1001_0000.toByte(), value shl 4) - assertEquals(0b1000_0000.toByte(), value shl 7) - assertEquals(0b0000_0000.toByte(), value shl 8) - } - - @Test - fun byteShr() { - val value = 0b1010_1001.toByte() - assertEquals(0b1101_0100.toByte(), value shr 1) - assertEquals(0b1111_1010.toByte(), value shr 4) - assertEquals(0b1111_1111.toByte(), value shr 7) - assertEquals(0b1111_1111.toByte(), value shr 8) - } - - @Test - fun byteUshr() { - val value = 0b1010_1001.toByte() - assertEquals(0b0101_0100.toByte(), value ushr 1) - assertEquals(0b0000_1010.toByte(), value ushr 4) - assertEquals(0b0000_0001.toByte(), value ushr 7) - assertEquals(0b0000_0000.toByte(), value ushr 8) - } - - @Test - fun uByteShl() { - val value = 0b1010_1001.toUByte() - assertEquals(0b0101_0010.toUByte(), value shl 1) - assertEquals(0b1001_0000.toUByte(), value shl 4) - assertEquals(0b1000_0000.toUByte(), value shl 7) - assertEquals(0b0000_0000.toUByte(), value shl 8) - } - - @Test - fun uByteShr() { - val value = 0b1010_1001.toUByte() - assertEquals(0b0101_0100.toUByte(), value shr 1) - assertEquals(0b0000_1010.toUByte(), value shr 4) - assertEquals(0b0000_0001.toUByte(), value shr 7) - assertEquals(0b0000_0000.toUByte(), value shr 8) - } - - @Test - fun shortShl() { - val value = 0b1010_1001_1010_1001.toShort() - assertEquals(0b0101_0011_0101_0010.toShort(), value shl 1) - assertEquals(0b1001_1010_1001_0000.toShort(), value shl 4) - assertEquals(0b1000_0000_0000_0000.toShort(), value shl 15) - assertEquals(0b0000_0000_0000_0000.toShort(), value shl 16) - } - - @Test - fun shortShr() { - val value = 0b1010_1001_1010_1001.toShort() - assertEquals(0b1101_0100_1101_0100.toShort(), value shr 1) - assertEquals(0b1111_1010_1001_1010.toShort(), value shr 4) - assertEquals(0b1111_1111_1111_0101.toShort(), value shr 11) - assertEquals(0b1111_1111_1111_1111.toShort(), value shr 16) - } - - @Test - fun shortUshr() { - val value = 0b1010_1001_1010_1001.toShort() - assertEquals(0b0101_0100_1101_0100.toShort(), value ushr 1) - assertEquals(0b0000_1010_1001_1010.toShort(), value ushr 4) - assertEquals(0b0000_0000_0001_0101.toShort(), value ushr 11) - assertEquals(0b0000_0000_0000_0000.toShort(), value ushr 16) - } - - @Test - fun uShortShl() { - val value = 0b1010_1001_1010_1001.toUShort() - assertEquals(0b0101_0011_0101_0010.toUShort(), value shl 1) - assertEquals(0b1001_1010_1001_0000.toUShort(), value shl 4) - assertEquals(0b1000_0000_0000_0000.toUShort(), value shl 15) - assertEquals(0b0000_0000_0000_0000.toUShort(), value shl 16) - } - - @Test - fun uShortShr() { - val value = 0b1010_1001_1010_1001.toUShort() - assertEquals(0b0101_0100_1101_0100.toUShort(), value shr 1) - assertEquals(0b0000_1010_1001_1010.toUShort(), value shr 4) - assertEquals(0b0000_0000_0001_0101.toUShort(), value shr 11) - assertEquals(0b0000_0000_0000_0000.toUShort(), value shr 16) - } -} \ No newline at end of file diff --git a/data/src/test/java/no/nordicsemi/kotlin/data/ByteArrayOperationsTest.kt b/data/src/test/java/no/nordicsemi/kotlin/data/ByteArrayOperationsTest.kt deleted file mode 100644 index c316cf00..00000000 --- a/data/src/test/java/no/nordicsemi/kotlin/data/ByteArrayOperationsTest.kt +++ /dev/null @@ -1,108 +0,0 @@ -package no.nordicsemi.kotlin.data - -import java.nio.ByteOrder -import kotlin.test.Test -import kotlin.test.assertContentEquals -import kotlin.test.assertEquals -import kotlin.test.assertFails - -internal class ByteArrayOperationsTest { - - @Test - fun intToByteArray() { - val value: Int = -2023406815 - val bigEndian = value.toByteArray(ByteOrder.BIG_ENDIAN) - val littleEndian = value.toByteArray(ByteOrder.LITTLE_ENDIAN) - assertContentEquals(byteArrayOf(0x87.toByte(), 0x65, 0x43, 0x21), bigEndian) - assertContentEquals(byteArrayOf(0x21, 0x43, 0x65, 0x87.toByte()), littleEndian) - } - - @Test - fun uIntToByteArray() { - val value = 2271560481u - val bigEndian = value.toByteArray(ByteOrder.BIG_ENDIAN) - val littleEndian = value.toByteArray(ByteOrder.LITTLE_ENDIAN) - assertContentEquals(byteArrayOf(0x87.toByte(), 0x65, 0x43, 0x21), bigEndian) - assertContentEquals(byteArrayOf(0x21, 0x43, 0x65, 0x87.toByte()), littleEndian) - } - - @Test - fun shortToByteArray() { - val value: Short = -30875 - val bigEndian = value.toByteArray(ByteOrder.BIG_ENDIAN) - val littleEndian = value.toByteArray(ByteOrder.LITTLE_ENDIAN) - assertContentEquals(byteArrayOf(0x87.toByte(), 0x65), bigEndian) - assertContentEquals(byteArrayOf(0x65, 0x87.toByte()), littleEndian) - } - - @Test - fun uShortToByteArray() { - val value: UShort = 34661u - val bigEndian = value.toByteArray(ByteOrder.BIG_ENDIAN) - val littleEndian = value.toByteArray(ByteOrder.LITTLE_ENDIAN) - assertContentEquals(byteArrayOf(0x87.toByte(), 0x65), bigEndian) - assertContentEquals(byteArrayOf(0x65, 0x87.toByte()), littleEndian) - } - - @Test - fun byteToByteArray() { - val value: Byte = 0x87.toByte() - val array = value.toByteArray() - assertContentEquals(byteArrayOf(0x87.toByte()), array) - } - - @Test - fun uByteToByteArray() { - val value: UByte = 0x87u - val array = value.toByteArray() - assertContentEquals(byteArrayOf(0x87.toByte()), array) - } - - @Test - fun getInt() { - val array = byteArrayOf(0x87.toByte(), 0x65, 0x43, 0x21, 0x21, 0x43, 0x65, 0x87.toByte()) - val int1 = array.getInt(0, ByteOrder.BIG_ENDIAN) - val int2 = array.getInt(4, ByteOrder.LITTLE_ENDIAN) - assertEquals(int1, -2023406815) - assertEquals(int2, -2023406815) - assertFails { - array.getInt(7, ByteOrder.LITTLE_ENDIAN) - } - } - - @Test - fun getUInt() { - val array = byteArrayOf(0x87.toByte(), 0x65, 0x43, 0x21, 0x21, 0x43, 0x65, 0x87.toByte()) - val int1 = array.getUInt(0, ByteOrder.BIG_ENDIAN) - val int2 = array.getUInt(4, ByteOrder.LITTLE_ENDIAN) - assertEquals(int1, 2271560481u) - assertEquals(int2, 2271560481u) - assertFails { - array.getUInt(7, ByteOrder.BIG_ENDIAN) - } - } - - @Test - fun getShort() { - val array = byteArrayOf(0x87.toByte(), 0x65, 0x65, 0x87.toByte()) - val short1 = array.getShort(0, ByteOrder.BIG_ENDIAN) - val short2 = array.getShort(2, ByteOrder.LITTLE_ENDIAN) - assertEquals(short1, (-30875).toShort()) - assertEquals(short2, (-30875).toShort()) - assertFails { - array.getShort(3, ByteOrder.BIG_ENDIAN) - } - } - - @Test - fun getUShort() { - val array = byteArrayOf(0x87.toByte(), 0x65, 0x65, 0x87.toByte()) - val short1 = array.getUShort(0, ByteOrder.BIG_ENDIAN) - val short2 = array.getUShort(2, ByteOrder.LITTLE_ENDIAN) - assertEquals(short1, 34661u.toUShort()) - assertEquals(short2, 34661u.toUShort()) - assertFails { - array.getUShort(3, ByteOrder.LITTLE_ENDIAN) - } - } -} \ No newline at end of file diff --git a/data/src/test/java/no/nordicsemi/kotlin/data/UuidOperationsKtTest.kt b/data/src/test/java/no/nordicsemi/kotlin/data/UuidOperationsKtTest.kt deleted file mode 100644 index 7399e99a..00000000 --- a/data/src/test/java/no/nordicsemi/kotlin/data/UuidOperationsKtTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -package no.nordicsemi.kotlin.data - - -import java.nio.ByteOrder -import java.util.UUID -import kotlin.test.Test -import kotlin.test.assertContentEquals -import kotlin.test.assertEquals - -internal class UuidOperationsKtTest { - - @Test - fun toByteArray_BigEndian() { - val uuid = UUID.fromString("00112233-4455-6677-8899-AABBCCDDEEFF") - val byteArray = uuid.toByteArray(ByteOrder.BIG_ENDIAN) - val expected = byteArrayOf( - 0x00, 0x11, 0x22, 0x33, - 0x44, 0x55, 0x66, 0x77, - 0x88.toByte(), 0x99.toByte(), 0xAA.toByte(), 0xBB.toByte(), - 0xCC.toByte(), 0xDD.toByte(), 0xEE.toByte(), 0xFF.toByte(), - ) - assertContentEquals(expected, byteArray) - } - - @Test - fun toByteArray_LittleEndian() { - val uuid = UUID.fromString("00001530-1212-EFDE-1523-785FEABCD123") // Legacy DFU Service UUID - val byteArray = uuid.toByteArray(ByteOrder.LITTLE_ENDIAN) - val expected = byteArrayOf( - 0x23, 0xD1.toByte(), 0xBC.toByte(), 0xEA.toByte(), - 0x5F, 0x78, 0x23, 0x15, - 0xDE.toByte(), 0xEF.toByte(), 0x12, 0x12, - 0x30, 0x15, 0x00, 0x00, - ) - assertContentEquals(expected, byteArray) - } - - @Test - fun getUuid_BigEndian() { - val array = byteArrayOf( - 0x00, 0x11, 0x22, 0x33, // offset - 0x00, 0x11, 0x22, 0x33, - 0x44, 0x55, 0x66, 0x77, - 0x88.toByte(), 0x99.toByte(), 0xAA.toByte(), 0xBB.toByte(), - 0xCC.toByte(), 0xDD.toByte(), 0xEE.toByte(), 0xFF.toByte(), - ) - val uuid = array.getUuid(4, ByteOrder.BIG_ENDIAN) - val expected = UUID.fromString("00112233-4455-6677-8899-AABBCCDDEEFF") - assertEquals(expected, uuid) - } - - @Test - fun getUuid_LittleEndian() { - val array = byteArrayOf( - 0x00, 0x11, 0x22, 0x33, // offset - 0x23, 0xD1.toByte(), 0xBC.toByte(), 0xEA.toByte(), - 0x5F, 0x78, 0x23, 0x15, - 0xDE.toByte(), 0xEF.toByte(), 0x12, 0x12, - 0x30, 0x15, 0x00, 0x00, - ) - val uuid = array.getUuid(4, ByteOrder.LITTLE_ENDIAN) - val expected = UUID.fromString("00001530-1212-EFDE-1523-785FEABCD123") - assertEquals(expected, uuid) - } - - @Test - fun toUuid_BigEndian() { - val array = byteArrayOf( - 0x00, 0x11, 0x22, 0x33, - 0x44, 0x55, 0x66, 0x77, - 0x88.toByte(), 0x99.toByte(), 0xAA.toByte(), 0xBB.toByte(), - 0xCC.toByte(), 0xDD.toByte(), 0xEE.toByte(), 0xFF.toByte(), - ) - val uuid = array.toUuid(ByteOrder.BIG_ENDIAN) - val expected = UUID.fromString("00112233-4455-6677-8899-AABBCCDDEEFF") - assertEquals(expected, uuid) - } - - @Test - fun toUuid_LittleEndian() { - val array = byteArrayOf( - 0x23, 0xD1.toByte(), 0xBC.toByte(), 0xEA.toByte(), - 0x5F, 0x78, 0x23, 0x15, - 0xDE.toByte(), 0xEF.toByte(), 0x12, 0x12, - 0x30, 0x15, 0x00, 0x00, - ) - val uuid = array.toUuid(ByteOrder.LITTLE_ENDIAN) - val expected = UUID.fromString("00001530-1212-EFDE-1523-785FEABCD123") - assertEquals(expected, uuid) - } -} \ No newline at end of file From 24d1deffa8a8fbb04da8a45f04b126b9848fd8f5 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Thu, 28 Mar 2024 14:58:11 +0100 Subject: [PATCH 07/27] Unused plugin removed --- build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 66e561e9..b43a5882 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,5 +42,4 @@ plugins { alias(libs.plugins.nordic.feature) apply false alias(libs.plugins.nordic.hilt) apply false alias(libs.plugins.nordic.nexus.android) apply false - alias(libs.plugins.nordic.nexus.kotlin) apply false } From f219c27c2984f46d538f90312dabb14ca19263aa Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Thu, 28 Mar 2024 14:49:14 +0100 Subject: [PATCH 08/27] New feature: FloatingActionMenu --- .../common/theme/view/FloatingActionMenu.kt | 248 ++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 theme/src/main/java/no/nordicsemi/android/common/theme/view/FloatingActionMenu.kt diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/FloatingActionMenu.kt b/theme/src/main/java/no/nordicsemi/android/common/theme/view/FloatingActionMenu.kt new file mode 100644 index 00000000..6cae78b4 --- /dev/null +++ b/theme/src/main/java/no/nordicsemi/android/common/theme/view/FloatingActionMenu.kt @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.common.theme.view + +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.FloatingActionButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onKeyEvent +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.findRootCoordinates +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.layout.positionInWindow +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.onClick +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.max +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties + +@Composable +fun FloatingActionMenu( + onDismissRequest: () -> Unit, + expanded: Boolean, + menuContent: @Composable ColumnScope.() -> Unit, + contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding, + content: @Composable () -> Unit, +) { + var bottomPadding by remember { mutableStateOf(0.dp) } + var endPadding by remember { mutableStateOf(0.dp) } + + if (expanded) { + Dialog( + onDismissRequest = onDismissRequest, + properties = DialogProperties( + usePlatformDefaultWidth = false, + ), + ) { + // By default, dialog's content is centered. We need to align it to the bottom-end, + // where the FAB usually is. + Box(contentAlignment = Alignment.BottomEnd) { + // The scrim takes all the available space and closes the dialog when clicked. + Scrim(onClose = onDismissRequest, modifier = Modifier.fillMaxSize()) + Column( + modifier = Modifier.padding(end = max(0.dp,endPadding - contentPadding.calculateEndPadding(LayoutDirection.Ltr))), + ) { + Column( + horizontalAlignment = Alignment.End, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + menuContent() + } + // The spacer positions the menu above the content. + Spacer(modifier = Modifier.height(bottomPadding - contentPadding.calculateBottomPadding())) + } + } + } + } + + val density = LocalDensity.current + val navigationBarHeight = WindowInsets.navigationBars.getBottom(density) + Box( + modifier = Modifier + .onGloballyPositioned { layoutCoordinates -> + with(density) { + val windowSize = layoutCoordinates.findRootCoordinates().size + val bottomOffset = + layoutCoordinates.positionInWindow().y + layoutCoordinates.size.height + val endOffset = + layoutCoordinates.positionInWindow().x + layoutCoordinates.size.width + bottomPadding = + windowSize.height.toDp() - bottomOffset.toDp() - navigationBarHeight.toDp() + endPadding = windowSize.width.toDp() - endOffset.toDp() + } + }, + ) { + content() + } +} + +@Composable +fun FloatingActionMenuItem( + text: @Composable () -> Unit, + icon: @Composable () -> Unit, + onClick: () -> Unit, + shape: Shape = FloatingActionButtonDefaults.shape, + contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding, +) { + TextButton( + onClick =onClick, + colors = ButtonDefaults.textButtonColors().copy( + contentColor = Color.White, + ), + shape = shape, + contentPadding = contentPadding, + ) { + text() + Spacer(modifier = Modifier.size(8.dp)) + icon() + } +} + +@Composable +fun FloatingActionMenuItem( + label: String, + imageVector: ImageVector, + onClick: () -> Unit, + shape: Shape = FloatingActionButtonDefaults.shape, + contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding, +) { + FloatingActionMenuItem( + text = { Text(text = label) }, + icon = { + Icon( + imageVector = imageVector, + contentDescription = label, + modifier = Modifier + .size(56.dp) + .background( + color = MaterialTheme.colorScheme.primaryContainer, + shape = shape, + ) + .padding(16.dp), + tint = MaterialTheme.colorScheme.onPrimaryContainer, + ) + }, + onClick = onClick, + shape = shape, + contentPadding = contentPadding, + ) +} + +@Composable +fun FloatingActionMenuItemSecondary( + label: String, + imageVector: ImageVector, + onClick: () -> Unit, + shape: Shape = FloatingActionButtonDefaults.smallShape, +) { + FloatingActionMenuItem( + text = { Text(text = label) }, + icon = { + Icon( + imageVector = imageVector, + contentDescription = label, + modifier = Modifier + .padding(start = 8.dp, end = 8.dp) + .size(40.dp) + .background( + color = MaterialTheme.colorScheme.surfaceContainer, + shape = shape, + ) + .padding(8.dp), + tint = MaterialTheme.colorScheme.onSurface, + ) + }, + onClick = onClick, + shape = shape, + ) +} + +// Source: https://github.com/android/snippets/blob/1cf58aeef8bb496e8f957ddba27519a63b50c48c/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/pointerinput/TapAndPress.kt#L296 +@Composable +private fun Scrim(onClose: () -> Unit, modifier: Modifier = Modifier) { + Box( + modifier = modifier + // handle pointer input + .pointerInput(onClose) { detectTapGestures { onClose() } } + // handle accessibility services + .semantics(mergeDescendants = true) { + contentDescription = "" + onClick { + onClose() + true + } + } + // handle physical keyboard input + .onKeyEvent { + if (it.key == Key.Escape) { + onClose() + true + } else { + false + } + } + ) +} \ No newline at end of file From 51d8bf0a493938245718af804ac61861526ed25f Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Thu, 28 Mar 2024 15:03:05 +0100 Subject: [PATCH 09/27] Example of using Floating Action Menu --- .../android/common/test/simple/Hello.kt | 61 ++++++++++++++++++- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/no/nordicsemi/android/common/test/simple/Hello.kt b/app/src/main/java/no/nordicsemi/android/common/test/simple/Hello.kt index b595b3cc..3da78bb9 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/simple/Hello.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/simple/Hello.kt @@ -35,11 +35,23 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.CopyAll +import androidx.compose.material.icons.filled.Restore import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button +import androidx.compose.material3.ExtendedFloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -52,6 +64,9 @@ import no.nordicsemi.android.common.navigation.defineDialogDestination import no.nordicsemi.android.common.navigation.viewmodel.SimpleNavigationViewModel import no.nordicsemi.android.common.test.R import no.nordicsemi.android.common.theme.NordicTheme +import no.nordicsemi.android.common.theme.view.FloatingActionMenu +import no.nordicsemi.android.common.theme.view.FloatingActionMenuItem +import no.nordicsemi.android.common.theme.view.FloatingActionMenuItemSecondary /** * This is an example of a simple destination. @@ -99,11 +114,51 @@ private fun HelloScreen( onShowDialog: () -> Unit, modifier: Modifier = Modifier, ) { - Column( + var showDialog by rememberSaveable { mutableStateOf(false) } + Scaffold( modifier = modifier, - ) { + floatingActionButton = { + FloatingActionMenu( + onDismissRequest = { showDialog = false }, + expanded = showDialog, + menuContent = { + FloatingActionMenuItemSecondary( + label = "Clone Tag", + imageVector = Icons.Default.CopyAll, + onClick = { /*TODO*/ } + ) + FloatingActionMenuItemSecondary( + label = "Existing Records", + imageVector = Icons.Default.Restore, + onClick = { /*TODO*/ } + ) + FloatingActionMenuItem( + label = "New Record", + imageVector = Icons.Default.Add, + onClick = { /*TODO*/ } + ) + } + ) { + ExtendedFloatingActionButton( + text = { Text(text = "Add Record") }, + icon = { + Icon( + imageVector = Icons.Default.Add, + contentDescription = null, + ) + }, + // Open the dialog when the FAB is clicked. + onClick = { showDialog = true }, + // Collapse the FAB when the dialog is shown. + expanded = !showDialog + ) + } + } + ) { paddingValues -> Column( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .padding(paddingValues), verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically), horizontalAlignment = Alignment.CenterHorizontally, ) { From a924f13602e0bebe08231a3b7c1c3ad3be64b40d Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Fri, 29 Mar 2024 12:38:01 +0100 Subject: [PATCH 10/27] Bug fixed in FloatingActionMenu positioning --- .../android/common/theme/view/FloatingActionMenu.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/FloatingActionMenu.kt b/theme/src/main/java/no/nordicsemi/android/common/theme/view/FloatingActionMenu.kt index 6cae78b4..f2aec6a4 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/FloatingActionMenu.kt +++ b/theme/src/main/java/no/nordicsemi/android/common/theme/view/FloatingActionMenu.kt @@ -68,7 +68,7 @@ import androidx.compose.ui.input.key.onKeyEvent import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.findRootCoordinates import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.layout.positionInWindow +import androidx.compose.ui.layout.positionInRoot import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.onClick @@ -112,7 +112,7 @@ fun FloatingActionMenu( menuContent() } // The spacer positions the menu above the content. - Spacer(modifier = Modifier.height(bottomPadding - contentPadding.calculateBottomPadding())) + Spacer(modifier = Modifier.height(max(0.dp, bottomPadding - contentPadding.calculateBottomPadding()))) } } } @@ -126,9 +126,9 @@ fun FloatingActionMenu( with(density) { val windowSize = layoutCoordinates.findRootCoordinates().size val bottomOffset = - layoutCoordinates.positionInWindow().y + layoutCoordinates.size.height + layoutCoordinates.positionInRoot().y + layoutCoordinates.size.height val endOffset = - layoutCoordinates.positionInWindow().x + layoutCoordinates.size.width + layoutCoordinates.positionInRoot().x + layoutCoordinates.size.width bottomPadding = windowSize.height.toDp() - bottomOffset.toDp() - navigationBarHeight.toDp() endPadding = windowSize.width.toDp() - endOffset.toDp() From 4bb79cb40bd3f7410d03b1f324dce18d2adcce5c Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 17 Apr 2024 22:07:43 +0200 Subject: [PATCH 11/27] Theme split into theme and ui --- analytics/build.gradle.kts | 2 +- .../view/AnalyticsPermissionAppBarButton.kt | 2 +- app/build.gradle.kts | 1 + .../android/common/test/MainActivity.kt | 50 +++++++++++++--- .../android/common/test/main/Main.kt | 4 +- .../common/test/main/page/BasicViewsPage.kt | 2 +- .../common/test/main/page/FontsPage.kt | 2 +- .../common/test/main/page/WarningPage.kt | 4 +- .../common/test/main/page/WizardPage.kt | 12 ++-- .../android/common/test/simple/Hello.kt | 6 +- logger/build.gradle.kts | 2 +- .../common/logger/view/LoggerAppBarIcon.kt | 2 +- .../common/navigation/NavigationView.kt | 1 - permissions-ble/build.gradle.kts | 2 +- .../ble/view/BluetoothDisabledView.kt | 6 +- .../ble/view/BluetoothNotAvailableView.kt | 6 +- .../view/BluetoothPermissionRequiredView.kt | 15 +---- .../view/LocationPermissionRequiredView.kt | 6 +- permissions-internet/build.gradle.kts | 2 +- .../internet/view/InternetNotAvailableView.kt | 6 +- permissions-nfc/build.gradle.kts | 2 +- .../permissions/nfc/view/NfcDisabledView.kt | 6 +- .../nfc/view/NfcNotAvailableView.kt | 7 +-- settings.gradle.kts | 1 + theme/build.gradle.kts | 4 -- theme/src/main/res/values-night/colors.xml | 3 +- .../main/res/values-night/colors_theme.xml | 2 - theme/src/main/res/values/colors.xml | 3 +- theme/src/main/res/values/colors_theme.xml | 2 - ui/build.gradle.kts | 59 +++++++++++++++++++ ui/module-rules.pro | 17 ++++++ ui/src/main/AndroidManifest.xml | 35 +++++++++++ .../android/common/ui}/view/AppBarIcon.kt | 2 +- .../android/common/ui}/view/CircularIcon.kt | 2 +- .../common/ui}/view/FloatingActionMenu.kt | 2 +- .../android/common/ui}/view/MarkdownText.kt | 8 +-- .../common/ui}/view/NavigationDrawerTitle.kt | 2 +- .../android/common/ui}/view/NordicAppBar.kt | 13 +++- .../common/ui}/view/NordicLargeAppBar.kt | 5 +- .../android/common/ui}/view/NordicLogo.kt | 4 +- .../common/ui}/view/NordicMediumAppBar.kt | 4 +- .../android/common/ui}/view/PagerView.kt | 4 +- .../android/common/ui}/view/ProgressItem.kt | 29 +++++---- .../common/ui}/view/RadioButtonGroup.kt | 2 +- .../android/common/ui}/view/RssiIcon.kt | 4 +- .../android/common/ui}/view/SectionTitle.kt | 7 ++- .../common/ui}/view/VerticalDivider.kt | 2 +- .../android/common/ui}/view/WarningView.kt | 12 ++-- .../common/ui}/view/WizardStepComponent.kt | 21 ++++++- .../common/ui}/view/internal/BigIcon.kt | 2 +- .../android/common/ui}/view/internal/Hint.kt | 2 +- .../android/common/ui}/view/internal/Title.kt | 2 +- ui/src/main/res/drawable/ic_arrow_right.xml | 40 +++++++++++++ ui/src/main/res/drawable/ic_check.xml | 40 +++++++++++++ ui/src/main/res/drawable/ic_cross.xml | 40 +++++++++++++ ui/src/main/res/drawable/ic_dot.xml | 40 +++++++++++++ ui/src/main/res/drawable/ic_signal_max.xml | 46 +++++++++++++++ ui/src/main/res/drawable/ic_signal_medium.xml | 46 +++++++++++++++ ui/src/main/res/drawable/ic_signal_min.xml | 46 +++++++++++++++ .../src/main/res/drawable}/nordic_logo.xml | 0 ui/src/main/res/values-night/colors.xml | 43 ++++++++++++++ ui/src/main/res/values/colors.xml | 43 ++++++++++++++ ui/src/main/res/values/strings.xml | 39 ++++++++++++ 63 files changed, 704 insertions(+), 122 deletions(-) create mode 100644 ui/build.gradle.kts create mode 100644 ui/module-rules.pro create mode 100644 ui/src/main/AndroidManifest.xml rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/AppBarIcon.kt (98%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/CircularIcon.kt (98%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/FloatingActionMenu.kt (99%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/MarkdownText.kt (91%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/NavigationDrawerTitle.kt (98%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/NordicAppBar.kt (90%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/NordicLargeAppBar.kt (97%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/NordicLogo.kt (95%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/NordicMediumAppBar.kt (98%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/PagerView.kt (98%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/ProgressItem.kt (86%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/RadioButtonGroup.kt (98%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/RssiIcon.kt (97%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/SectionTitle.kt (92%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/VerticalDivider.kt (98%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/WarningView.kt (93%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/WizardStepComponent.kt (87%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/internal/BigIcon.kt (98%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/internal/Hint.kt (97%) rename {theme/src/main/java/no/nordicsemi/android/common/theme => ui/src/main/java/no/nordicsemi/android/common/ui}/view/internal/Title.kt (97%) create mode 100644 ui/src/main/res/drawable/ic_arrow_right.xml create mode 100644 ui/src/main/res/drawable/ic_check.xml create mode 100644 ui/src/main/res/drawable/ic_cross.xml create mode 100644 ui/src/main/res/drawable/ic_dot.xml create mode 100644 ui/src/main/res/drawable/ic_signal_max.xml create mode 100644 ui/src/main/res/drawable/ic_signal_medium.xml create mode 100644 ui/src/main/res/drawable/ic_signal_min.xml rename {theme/src/main/res/drawable-anydpi => ui/src/main/res/drawable}/nordic_logo.xml (100%) create mode 100644 ui/src/main/res/values-night/colors.xml create mode 100644 ui/src/main/res/values/colors.xml create mode 100644 ui/src/main/res/values/strings.xml diff --git a/analytics/build.gradle.kts b/analytics/build.gradle.kts index cb30cc24..c7ba613a 100644 --- a/analytics/build.gradle.kts +++ b/analytics/build.gradle.kts @@ -54,7 +54,7 @@ android { dependencies { implementation(project(":core")) - implementation(project(":theme")) + implementation(project(":ui")) implementation(libs.androidx.dataStore.preferences) implementation(libs.androidx.hilt.navigation.compose) diff --git a/analytics/src/main/java/no/nordicsemi/android/common/analytics/view/AnalyticsPermissionAppBarButton.kt b/analytics/src/main/java/no/nordicsemi/android/common/analytics/view/AnalyticsPermissionAppBarButton.kt index 0e4a12c0..c4ca47aa 100644 --- a/analytics/src/main/java/no/nordicsemi/android/common/analytics/view/AnalyticsPermissionAppBarButton.kt +++ b/analytics/src/main/java/no/nordicsemi/android/common/analytics/view/AnalyticsPermissionAppBarButton.kt @@ -40,7 +40,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.common.analytics.R -import no.nordicsemi.android.common.theme.view.AppBarIcon +import no.nordicsemi.android.common.ui.view.AppBarIcon @Composable fun AnalyticsPermissionButton() { diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b1daa24f..18eb7787 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -44,6 +44,7 @@ dependencies { implementation(project(":logger")) implementation(project(":theme")) + implementation(project(":ui")) implementation(project(":navigation")) implementation(project(":permissions-ble")) implementation(project(":permissions-nfc")) diff --git a/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt b/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt index 19c45f5a..8af93321 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt @@ -35,12 +35,31 @@ import android.annotation.SuppressLint import android.os.Bundle import android.widget.Toast import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.* +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.* -import androidx.compose.material3.* +import androidx.compose.material.icons.filled.Album +import androidx.compose.material.icons.filled.BrokenImage +import androidx.compose.material.icons.filled.ImageSearch +import androidx.compose.material.icons.filled.Navigation +import androidx.compose.material.icons.filled.Settings +import androidx.compose.material.icons.filled.Verified +import androidx.compose.material3.DrawerValue +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.ModalDrawerSheet +import androidx.compose.material3.ModalNavigationDrawer +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.NavigationDrawerItem +import androidx.compose.material3.NavigationDrawerItemDefaults +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope @@ -54,14 +73,30 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import no.nordicsemi.android.common.logger.view.LoggerAppBarIcon -import no.nordicsemi.android.common.navigation.* +import no.nordicsemi.android.common.navigation.DestinationId +import no.nordicsemi.android.common.navigation.NavigationView +import no.nordicsemi.android.common.navigation.Navigator +import no.nordicsemi.android.common.navigation.createSimpleDestination +import no.nordicsemi.android.common.navigation.popUpToStartDestination import no.nordicsemi.android.common.navigation.viewmodel.SimpleNavigationViewModel +import no.nordicsemi.android.common.navigation.with import no.nordicsemi.android.common.test.main.MainDestinations -import no.nordicsemi.android.common.test.simple.* -import no.nordicsemi.android.common.test.tab.* +import no.nordicsemi.android.common.test.simple.Advanced +import no.nordicsemi.android.common.test.simple.AdvancedDestination +import no.nordicsemi.android.common.test.simple.Hello +import no.nordicsemi.android.common.test.simple.HelloDialog +import no.nordicsemi.android.common.test.simple.Settings +import no.nordicsemi.android.common.test.simple.SettingsDestination +import no.nordicsemi.android.common.test.tab.SecondDestinations +import no.nordicsemi.android.common.test.tab.ThirdDestination +import no.nordicsemi.android.common.test.tab.ThirdTab import no.nordicsemi.android.common.theme.NordicActivity import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.* +import no.nordicsemi.android.common.ui.view.NavigationDrawerDividerDefaults +import no.nordicsemi.android.common.ui.view.NavigationDrawerTitle +import no.nordicsemi.android.common.ui.view.NavigationDrawerTitleDefaults +import no.nordicsemi.android.common.ui.view.NordicAppBar +import no.nordicsemi.android.common.ui.view.NordicLogo data class Item(val title: String, val destinationId: DestinationId, val icon: ImageVector) @@ -77,6 +112,7 @@ class MainActivity : NordicActivity() { super.onCreate(savedInstanceState) setDecorFitsSystemWindows(false) + enableEdgeToEdge() // <- This is the Way. val menuItems = listOf( Item("Main", Tabs, Icons.Filled.Verified), diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt index 680829a4..3d310d5c 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt @@ -41,8 +41,8 @@ import no.nordicsemi.android.common.test.main.page.FontsPage import no.nordicsemi.android.common.test.main.page.WarningPage import no.nordicsemi.android.common.test.main.page.WizardPage import no.nordicsemi.android.common.test.simple.HelloDestinations -import no.nordicsemi.android.common.theme.view.PagerView -import no.nordicsemi.android.common.theme.view.PagerViewEntity +import no.nordicsemi.android.common.ui.view.PagerView +import no.nordicsemi.android.common.ui.view.PagerViewEntity /** This is the destination identifier. */ val Main = createSimpleDestination("main") diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt index 9175d4da..a4e229f5 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/page/BasicViewsPage.kt @@ -67,7 +67,7 @@ import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.test.R import no.nordicsemi.android.common.test.simple.Hello import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.PagerViewItem +import no.nordicsemi.android.common.ui.view.PagerViewItem import javax.inject.Inject val BasicsPage = PagerViewItem("Basics") { diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/page/FontsPage.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/page/FontsPage.kt index 819e28e7..082fa4cf 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/page/FontsPage.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/page/FontsPage.kt @@ -41,7 +41,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.test.R import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.PagerViewItem +import no.nordicsemi.android.common.ui.view.PagerViewItem val FontsPage = PagerViewItem("Fonts") { FontsScreen() diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/page/WarningPage.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/page/WarningPage.kt index 5a495041..232dc416 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/page/WarningPage.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/page/WarningPage.kt @@ -47,8 +47,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.test.R import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.PagerViewItem -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.PagerViewItem +import no.nordicsemi.android.common.ui.view.WarningView val WarningPage = PagerViewItem("Warning") { WarningScreen() diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt index 50a80b35..58e4c3fa 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt @@ -60,12 +60,12 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.test.R import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.PagerViewItem -import no.nordicsemi.android.common.theme.view.ProgressItem -import no.nordicsemi.android.common.theme.view.ProgressItemStatus -import no.nordicsemi.android.common.theme.view.WizardStepAction -import no.nordicsemi.android.common.theme.view.WizardStepComponent -import no.nordicsemi.android.common.theme.view.WizardStepState +import no.nordicsemi.android.common.ui.view.PagerViewItem +import no.nordicsemi.android.common.ui.view.ProgressItem +import no.nordicsemi.android.common.ui.view.ProgressItemStatus +import no.nordicsemi.android.common.ui.view.WizardStepAction +import no.nordicsemi.android.common.ui.view.WizardStepComponent +import no.nordicsemi.android.common.ui.view.WizardStepState val WizardPage = PagerViewItem("Wizard") { WizardScreen() diff --git a/app/src/main/java/no/nordicsemi/android/common/test/simple/Hello.kt b/app/src/main/java/no/nordicsemi/android/common/test/simple/Hello.kt index 3da78bb9..f0ae2dbc 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/simple/Hello.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/simple/Hello.kt @@ -64,9 +64,9 @@ import no.nordicsemi.android.common.navigation.defineDialogDestination import no.nordicsemi.android.common.navigation.viewmodel.SimpleNavigationViewModel import no.nordicsemi.android.common.test.R import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.FloatingActionMenu -import no.nordicsemi.android.common.theme.view.FloatingActionMenuItem -import no.nordicsemi.android.common.theme.view.FloatingActionMenuItemSecondary +import no.nordicsemi.android.common.ui.view.FloatingActionMenu +import no.nordicsemi.android.common.ui.view.FloatingActionMenuItem +import no.nordicsemi.android.common.ui.view.FloatingActionMenuItemSecondary /** * This is an example of a simple destination. diff --git a/logger/build.gradle.kts b/logger/build.gradle.kts index 0cac9765..8e71afd1 100644 --- a/logger/build.gradle.kts +++ b/logger/build.gradle.kts @@ -53,7 +53,7 @@ nordicNexusPublishing { } dependencies { - implementation(project(":theme")) + implementation(project(":ui")) api(libs.nordic.log) } diff --git a/logger/src/main/java/no/nordicsemi/android/common/logger/view/LoggerAppBarIcon.kt b/logger/src/main/java/no/nordicsemi/android/common/logger/view/LoggerAppBarIcon.kt index 9393fa99..4d7e1ee6 100644 --- a/logger/src/main/java/no/nordicsemi/android/common/logger/view/LoggerAppBarIcon.kt +++ b/logger/src/main/java/no/nordicsemi/android/common/logger/view/LoggerAppBarIcon.kt @@ -35,7 +35,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import no.nordicsemi.android.common.logger.R -import no.nordicsemi.android.common.theme.view.AppBarIcon +import no.nordicsemi.android.common.ui.view.AppBarIcon @Composable fun LoggerAppBarIcon( diff --git a/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt index b240baae..5fbbe606 100644 --- a/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt +++ b/navigation/src/main/java/no/nordicsemi/android/common/navigation/NavigationView.kt @@ -133,7 +133,6 @@ private fun NavGraphBuilder.create( is ComposableDestination -> { composable( route = destination.id.name, - ) { navigation.use(it.savedStateHandle) destination.content() diff --git a/permissions-ble/build.gradle.kts b/permissions-ble/build.gradle.kts index 3b22414a..c34119ff 100644 --- a/permissions-ble/build.gradle.kts +++ b/permissions-ble/build.gradle.kts @@ -54,7 +54,7 @@ android { dependencies { implementation(project(":core")) - implementation(project(":theme")) + implementation(project(":ui")) implementation(project(":navigation")) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt index 90235d70..a10934cd 100644 --- a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt +++ b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt @@ -41,6 +41,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.BluetoothDisabled import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -48,8 +49,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.common.permissions.ble.R -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView @Composable internal fun BluetoothDisabledView() { @@ -76,7 +76,7 @@ private fun enableBluetooth(context: Context) { @Preview @Composable private fun BluetoothDisabledView_Preview() { - NordicTheme { + MaterialTheme { BluetoothDisabledView() } } diff --git a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt index 87a61a6a..2b6a3fb9 100644 --- a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt +++ b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt @@ -36,13 +36,13 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.BluetoothDisabled +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.common.permissions.ble.R -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView @Composable internal fun BluetoothNotAvailableView() { @@ -59,7 +59,7 @@ internal fun BluetoothNotAvailableView() { @Preview @Composable private fun BluetoothNotAvailableView_Preview() { - NordicTheme { + MaterialTheme { BluetoothNotAvailableView() } } diff --git a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothPermissionRequiredView.kt b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothPermissionRequiredView.kt index f97859a8..616ff3e6 100644 --- a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothPermissionRequiredView.kt +++ b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothPermissionRequiredView.kt @@ -55,13 +55,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview import androidx.core.content.ContextCompat import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.permissions.ble.R import no.nordicsemi.android.common.permissions.ble.viewmodel.PermissionViewModel -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView @RequiresApi(Build.VERSION_CODES.S) @Composable @@ -113,13 +111,4 @@ private fun openPermissionSettings(context: Context) { ), null ) -} - -@RequiresApi(Build.VERSION_CODES.S) -@Preview -@Composable -private fun BluetoothPermissionRequiredView_Preview() { - NordicTheme { - BluetoothPermissionRequiredView() - } -} +} \ No newline at end of file diff --git a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt index e004ca30..12b22e09 100644 --- a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt +++ b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt @@ -44,6 +44,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.LocationOff import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -59,8 +60,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.core.parseBold import no.nordicsemi.android.common.permissions.ble.R import no.nordicsemi.android.common.permissions.ble.viewmodel.PermissionViewModel -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView @Composable internal fun LocationPermissionRequiredView() { @@ -128,7 +128,7 @@ private fun openPermissionSettings(context: Context) { @Preview @Composable private fun LocationPermissionRequiredView_Preview() { - NordicTheme { + MaterialTheme { LocationPermissionRequiredView( permissionDenied = false, onGrantClicked = { }, diff --git a/permissions-internet/build.gradle.kts b/permissions-internet/build.gradle.kts index 9e13362a..79800548 100644 --- a/permissions-internet/build.gradle.kts +++ b/permissions-internet/build.gradle.kts @@ -54,7 +54,7 @@ android { dependencies { implementation(project(":core")) - implementation(project(":theme")) + implementation(project(":ui")) implementation(project(":navigation")) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt b/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt index c48ca241..813b58f7 100644 --- a/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt +++ b/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt @@ -36,13 +36,13 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.BluetoothDisabled +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.common.permissions.internet.R -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView @Composable internal fun InternetNotAvailableView() { @@ -59,7 +59,7 @@ internal fun InternetNotAvailableView() { @Preview @Composable private fun BluetoothNotAvailableView_Preview() { - NordicTheme { + MaterialTheme { InternetNotAvailableView() } } diff --git a/permissions-nfc/build.gradle.kts b/permissions-nfc/build.gradle.kts index 5ad6a5eb..beb22a76 100644 --- a/permissions-nfc/build.gradle.kts +++ b/permissions-nfc/build.gradle.kts @@ -54,7 +54,7 @@ android { dependencies { implementation(project(":core")) - implementation(project(":theme")) + implementation(project(":ui")) implementation(project(":navigation")) implementation(libs.androidx.navigation.compose) diff --git a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt index c32f19cb..07c4cc6d 100644 --- a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt +++ b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt @@ -38,6 +38,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -46,8 +47,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.common.permissions.nfc.R -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView @Composable internal fun NfcDisabledView() { @@ -73,7 +73,7 @@ private fun enableNfc(context: Context) { @Preview @Composable private fun NfcDisabledView_Preview() { - NordicTheme { + MaterialTheme { NfcDisabledView() } } diff --git a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt index f5335fc1..47ba9298 100644 --- a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt +++ b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt @@ -34,15 +34,14 @@ package no.nordicsemi.android.common.permissions.nfc.view import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.common.permissions.nfc.R -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView - +import no.nordicsemi.android.common.ui.view.WarningView @Composable internal fun NfcNotAvailableView() { @@ -59,7 +58,7 @@ internal fun NfcNotAvailableView() { @Preview @Composable private fun NfcNotAvailableView_Preview() { - NordicTheme { + MaterialTheme { NfcNotAvailableView() } } diff --git a/settings.gradle.kts b/settings.gradle.kts index dd4d70f1..95f89769 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -53,6 +53,7 @@ rootProject.name = "Common Libraries" include(":app") include(":core") include(":theme") +include(":ui") include(":logger") include(":navigation") include(":analytics") diff --git a/theme/build.gradle.kts b/theme/build.gradle.kts index 7a56f49c..056902f8 100644 --- a/theme/build.gradle.kts +++ b/theme/build.gradle.kts @@ -53,10 +53,6 @@ android { } dependencies { - implementation(libs.androidx.compose.material.iconsExtended) - implementation(libs.androidx.activity.compose) implementation(libs.androidx.core.splashscreen) - - implementation(libs.markdown) } diff --git a/theme/src/main/res/values-night/colors.xml b/theme/src/main/res/values-night/colors.xml index 2c9a70b6..74b7a680 100644 --- a/theme/src/main/res/values-night/colors.xml +++ b/theme/src/main/res/values-night/colors.xml @@ -32,8 +32,7 @@ - @color/nordicDarkGray - @color/appBarColor + @color/nordicDarkGray #17282C \ No newline at end of file diff --git a/theme/src/main/res/values-night/colors_theme.xml b/theme/src/main/res/values-night/colors_theme.xml index 4b411c01..ddc88211 100644 --- a/theme/src/main/res/values-night/colors_theme.xml +++ b/theme/src/main/res/values-night/colors_theme.xml @@ -44,8 +44,6 @@ #FFB2B6 #670016 #FFB4A9 - @color/nordicFall - @color/nordicGreen #930006 @android:color/black #FFDAD4 diff --git a/theme/src/main/res/values/colors.xml b/theme/src/main/res/values/colors.xml index e924bff6..228beb6e 100644 --- a/theme/src/main/res/values/colors.xml +++ b/theme/src/main/res/values/colors.xml @@ -32,8 +32,7 @@ - @color/md_theme_primary - @color/appBarColor + @color/md_theme_primary #E1EFF2 @color/md_theme_onBackground diff --git a/theme/src/main/res/values/colors_theme.xml b/theme/src/main/res/values/colors_theme.xml index 4c7aa2ed..17a7a737 100644 --- a/theme/src/main/res/values/colors_theme.xml +++ b/theme/src/main/res/values/colors_theme.xml @@ -44,8 +44,6 @@ #D0E4FF #41000A #BA1B1B - @color/nordicFall - @color/nordicGreen #FFDAD4 @android:color/white #410001 diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts new file mode 100644 index 00000000..af1679be --- /dev/null +++ b/ui/build.gradle.kts @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +plugins { + alias(libs.plugins.nordic.library.compose) + alias(libs.plugins.nordic.hilt) + alias(libs.plugins.nordic.nexus.android) +} + +group = "no.nordicsemi.android.common" + +nordicNexusPublishing { + POM_ARTIFACT_ID = "theme" + POM_NAME = "Nordic UI library for Android." + + POM_DESCRIPTION = "Nordic Android Common Libraries" + POM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" + POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" + POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" + POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" +} + +android { + namespace = "no.nordicsemi.android.common.ui" +} + +dependencies { + implementation(libs.androidx.compose.material.iconsExtended) + + implementation(libs.markdown) +} diff --git a/ui/module-rules.pro b/ui/module-rules.pro new file mode 100644 index 00000000..7e9d74c9 --- /dev/null +++ b/ui/module-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:/Users/alno/AppData/Local/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} \ No newline at end of file diff --git a/ui/src/main/AndroidManifest.xml b/ui/src/main/AndroidManifest.xml new file mode 100644 index 00000000..30c68711 --- /dev/null +++ b/ui/src/main/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/AppBarIcon.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/AppBarIcon.kt similarity index 98% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/AppBarIcon.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/AppBarIcon.kt index 51e9528a..170f3952 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/AppBarIcon.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/AppBarIcon.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/CircularIcon.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/CircularIcon.kt similarity index 98% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/CircularIcon.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/CircularIcon.kt index 1d1fa254..a0bb0057 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/CircularIcon.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/CircularIcon.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.Image import androidx.compose.foundation.background diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/FloatingActionMenu.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/FloatingActionMenu.kt similarity index 99% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/FloatingActionMenu.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/FloatingActionMenu.kt index f2aec6a4..d5a15814 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/FloatingActionMenu.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/FloatingActionMenu.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/MarkdownText.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/MarkdownText.kt similarity index 91% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/MarkdownText.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/MarkdownText.kt index f7ce33ef..ae275eb3 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/MarkdownText.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/MarkdownText.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme @@ -42,12 +42,10 @@ import dev.jeziellago.compose.markdowntext.MarkdownText fun NordicText( text: String, modifier: Modifier = Modifier, - style: TextStyle = MaterialTheme.typography.bodyMedium + style: TextStyle = MaterialTheme.typography.bodyMedium.copy(color = LocalContentColor.current) ) { MarkdownText( markdown = text, - color = LocalContentColor.current, style = style, - modifier = modifier - ) + modifier = modifier) } diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/NavigationDrawerTitle.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NavigationDrawerTitle.kt similarity index 98% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/NavigationDrawerTitle.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/NavigationDrawerTitle.kt index 64577fb4..f48f322e 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/NavigationDrawerTitle.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NavigationDrawerTitle.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicAppBar.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicAppBar.kt similarity index 90% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicAppBar.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicAppBar.kt index 29b0362b..82b4764c 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicAppBar.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicAppBar.kt @@ -29,21 +29,28 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Menu -import androidx.compose.material3.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp -import no.nordicsemi.android.common.theme.R +import no.nordicsemi.android.common.ui.R @ExperimentalMaterial3Api @Composable diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicLargeAppBar.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt similarity index 97% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicLargeAppBar.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt index 63a5b5fd..a84fd513 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicLargeAppBar.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt @@ -31,7 +31,7 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets @@ -43,6 +43,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable @@ -51,7 +52,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp -import no.nordicsemi.android.common.theme.R +import no.nordicsemi.android.common.ui.R @Composable fun NordicLargeAppBar( diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicLogo.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLogo.kt similarity index 95% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicLogo.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLogo.kt index c035c1d5..367f696e 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicLogo.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLogo.kt @@ -29,14 +29,14 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import no.nordicsemi.android.common.theme.R +import no.nordicsemi.android.common.ui.R @Composable fun NordicLogo( diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicMediumAppBar.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicMediumAppBar.kt similarity index 98% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicMediumAppBar.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicMediumAppBar.kt index 4eba37b4..85debdc2 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/NordicMediumAppBar.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicMediumAppBar.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets @@ -48,7 +48,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp -import no.nordicsemi.android.common.theme.R +import no.nordicsemi.android.common.ui.R @ExperimentalMaterial3Api @Composable diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/PagerView.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/PagerView.kt similarity index 98% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/PagerView.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/PagerView.kt index 6607e69d..c335f897 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/PagerView.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/PagerView.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -53,7 +53,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import no.nordicsemi.android.common.theme.R +import no.nordicsemi.android.common.ui.R class PagerViewEntity( val items: List, diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/ProgressItem.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt similarity index 86% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/ProgressItem.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt index 046af06d..4e531604 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/ProgressItem.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt @@ -29,11 +29,21 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.annotation.DrawableRes -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ProvideTextStyle +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -44,8 +54,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.R +import no.nordicsemi.android.common.ui.R enum class ProgressItemStatus { DISABLED, WORKING, SUCCESS, ERROR @@ -88,7 +97,7 @@ private fun ProgressItemStatus.toIconColor(): Color { return when (this) { ProgressItemStatus.DISABLED -> MaterialTheme.colorScheme.surfaceVariant ProgressItemStatus.WORKING -> LocalContentColor.current - ProgressItemStatus.SUCCESS -> colorResource(id = R.color.nordicGrass) + ProgressItemStatus.SUCCESS -> colorResource(id = R.color.colorSuccess) ProgressItemStatus.ERROR -> MaterialTheme.colorScheme.error } } @@ -116,7 +125,7 @@ private fun ProgressItemStatus.toImageRes(): Int { @Preview @Composable private fun ProgressItemPreview_Working() { - NordicTheme { + MaterialTheme { ProgressItem( text = "Uploading", status = ProgressItemStatus.WORKING, @@ -137,7 +146,7 @@ private fun ProgressItemPreview_Working() { @Preview @Composable private fun ProgressItemPreview_Success() { - NordicTheme { + MaterialTheme { ProgressItem( text = "Completed", status = ProgressItemStatus.SUCCESS, @@ -148,7 +157,7 @@ private fun ProgressItemPreview_Success() { @Preview @Composable private fun ProgressItemPreview_Disabled() { - NordicTheme { + MaterialTheme { ProgressItem( text = "Disabled", status = ProgressItemStatus.DISABLED, @@ -159,7 +168,7 @@ private fun ProgressItemPreview_Disabled() { @Preview @Composable private fun ProgressItemPreview_Error() { - NordicTheme { + MaterialTheme { ProgressItem( text = "Error: Too bad", status = ProgressItemStatus.ERROR, diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/RadioButtonGroup.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RadioButtonGroup.kt similarity index 98% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/RadioButtonGroup.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/RadioButtonGroup.kt index 625d09d6..91ba4c21 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/RadioButtonGroup.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RadioButtonGroup.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/RssiIcon.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RssiIcon.kt similarity index 97% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/RssiIcon.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/RssiIcon.kt index 4056636c..eae2d601 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/RssiIcon.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RssiIcon.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.annotation.DrawableRes import androidx.compose.foundation.Image @@ -45,7 +45,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import no.nordicsemi.android.common.theme.R +import no.nordicsemi.android.common.ui.R private const val MEDIUM_RSSI = -80 private const val MAX_RSSI = -60 diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/SectionTitle.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/SectionTitle.kt similarity index 92% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/SectionTitle.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/SectionTitle.kt index 19179b9e..a89897f2 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/SectionTitle.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/SectionTitle.kt @@ -29,9 +29,12 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/VerticalDivider.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/VerticalDivider.kt similarity index 98% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/VerticalDivider.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/VerticalDivider.kt index b39d6674..71735dd2 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/VerticalDivider.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/VerticalDivider.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/WarningView.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WarningView.kt similarity index 93% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/WarningView.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/WarningView.kt index 1bafd850..2f2dd71e 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/WarningView.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WarningView.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -37,6 +37,7 @@ import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -46,10 +47,9 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.internal.BigIcon -import no.nordicsemi.android.common.theme.view.internal.Hint -import no.nordicsemi.android.common.theme.view.internal.Title +import no.nordicsemi.android.common.ui.view.internal.BigIcon +import no.nordicsemi.android.common.ui.view.internal.Hint +import no.nordicsemi.android.common.ui.view.internal.Title @Composable fun WarningView( @@ -126,7 +126,7 @@ fun WarningView( @Preview @Composable private fun WarningViewPreview() { - NordicTheme { + MaterialTheme { WarningView( imageVector = Icons.Filled.Warning, title = "Warning", diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/WizardStepComponent.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt similarity index 87% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/WizardStepComponent.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt index 4ae72c23..b8d20ae7 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/WizardStepComponent.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt @@ -29,10 +29,25 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view +package no.nordicsemi.android.common.ui.view -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/internal/BigIcon.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/internal/BigIcon.kt similarity index 98% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/internal/BigIcon.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/internal/BigIcon.kt index 22f18434..83b3b0c4 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/internal/BigIcon.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/internal/BigIcon.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view.internal +package no.nordicsemi.android.common.ui.view.internal import androidx.compose.foundation.Image import androidx.compose.foundation.layout.size diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/internal/Hint.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/internal/Hint.kt similarity index 97% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/internal/Hint.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/internal/Hint.kt index d20b83b2..2415c8db 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/internal/Hint.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/internal/Hint.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view.internal +package no.nordicsemi.android.common.ui.view.internal import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/view/internal/Title.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/internal/Title.kt similarity index 97% rename from theme/src/main/java/no/nordicsemi/android/common/theme/view/internal/Title.kt rename to ui/src/main/java/no/nordicsemi/android/common/ui/view/internal/Title.kt index 681586a1..e4acf2e0 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/view/internal/Title.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/internal/Title.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.theme.view.internal +package no.nordicsemi.android.common.ui.view.internal import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text diff --git a/ui/src/main/res/drawable/ic_arrow_right.xml b/ui/src/main/res/drawable/ic_arrow_right.xml new file mode 100644 index 00000000..035ce6f5 --- /dev/null +++ b/ui/src/main/res/drawable/ic_arrow_right.xml @@ -0,0 +1,40 @@ + + + + + diff --git a/ui/src/main/res/drawable/ic_check.xml b/ui/src/main/res/drawable/ic_check.xml new file mode 100644 index 00000000..d3a4543b --- /dev/null +++ b/ui/src/main/res/drawable/ic_check.xml @@ -0,0 +1,40 @@ + + + + + diff --git a/ui/src/main/res/drawable/ic_cross.xml b/ui/src/main/res/drawable/ic_cross.xml new file mode 100644 index 00000000..2f6612a4 --- /dev/null +++ b/ui/src/main/res/drawable/ic_cross.xml @@ -0,0 +1,40 @@ + + + + + diff --git a/ui/src/main/res/drawable/ic_dot.xml b/ui/src/main/res/drawable/ic_dot.xml new file mode 100644 index 00000000..abf7bc2b --- /dev/null +++ b/ui/src/main/res/drawable/ic_dot.xml @@ -0,0 +1,40 @@ + + + + + diff --git a/ui/src/main/res/drawable/ic_signal_max.xml b/ui/src/main/res/drawable/ic_signal_max.xml new file mode 100644 index 00000000..c44a1f32 --- /dev/null +++ b/ui/src/main/res/drawable/ic_signal_max.xml @@ -0,0 +1,46 @@ + + + + + + + diff --git a/ui/src/main/res/drawable/ic_signal_medium.xml b/ui/src/main/res/drawable/ic_signal_medium.xml new file mode 100644 index 00000000..c0521bfa --- /dev/null +++ b/ui/src/main/res/drawable/ic_signal_medium.xml @@ -0,0 +1,46 @@ + + + + + + + diff --git a/ui/src/main/res/drawable/ic_signal_min.xml b/ui/src/main/res/drawable/ic_signal_min.xml new file mode 100644 index 00000000..93ac33fa --- /dev/null +++ b/ui/src/main/res/drawable/ic_signal_min.xml @@ -0,0 +1,46 @@ + + + + + + + diff --git a/theme/src/main/res/drawable-anydpi/nordic_logo.xml b/ui/src/main/res/drawable/nordic_logo.xml similarity index 100% rename from theme/src/main/res/drawable-anydpi/nordic_logo.xml rename to ui/src/main/res/drawable/nordic_logo.xml diff --git a/ui/src/main/res/values-night/colors.xml b/ui/src/main/res/values-night/colors.xml new file mode 100644 index 00000000..41ba85ca --- /dev/null +++ b/ui/src/main/res/values-night/colors.xml @@ -0,0 +1,43 @@ + + + + + + #333f48 + #FFB4A9 + #F58220 + #00A651 + #899296 + @android:color/white + @android:color/white + + \ No newline at end of file diff --git a/ui/src/main/res/values/colors.xml b/ui/src/main/res/values/colors.xml new file mode 100644 index 00000000..c12ccc33 --- /dev/null +++ b/ui/src/main/res/values/colors.xml @@ -0,0 +1,43 @@ + + + + + + #00A9CE + #BA1B1B + #F58220 + #00A651 + #70787C + #00A9CE + @android:color/black + + \ No newline at end of file diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml new file mode 100644 index 00000000..1299ed8f --- /dev/null +++ b/ui/src/main/res/values/strings.xml @@ -0,0 +1,39 @@ + + + + + Nordic Semiconductor + Back + Menu + + %d dBm + From 18ac5b88be09ed450d7b2726857d250a62f5fa06 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 12 Jun 2024 13:26:02 +0200 Subject: [PATCH 12/27] Scanner (not working) --- scanner/build.gradle.kts | 64 ++++++++ scanner/src/main/AndroidManifest.xml | 84 ++++++++++ .../kotlin/ble/ui/scanner/HiltModule.kt | 53 ++++++ .../kotlin/ble/ui/scanner/ScannerScreen.kt | 70 ++++++++ .../ble/ui/scanner/ScannerScreenResult.kt | 40 +++++ .../kotlin/ble/ui/scanner/ScannerView.kt | 124 ++++++++++++++ .../ble/ui/scanner/di/ScannerHiltModule.kt | 55 +++++++ .../ble/ui/scanner/main/DeviceListItem.kt | 107 ++++++++++++ .../ble/ui/scanner/main/DeviceListItems.kt | 100 +++++++++++ .../ble/ui/scanner/main/DevicesListView.kt | 120 ++++++++++++++ .../main/viewmodel/ScannerViewModel.kt | 131 +++++++++++++++ .../scanner/repository/DevicesScanFilter.kt | 38 +++++ .../scanner/repository/ScannerRepository.kt | 46 ++++++ .../ui/scanner/repository/ScanningState.kt | 55 +++++++ .../ui/scanner/view/DeviceConnectingView.kt | 116 +++++++++++++ .../ui/scanner/view/DeviceDisconnectedView.kt | 155 ++++++++++++++++++ .../ble/ui/scanner/view/ScannerAppBar.kt | 68 ++++++++ .../ui/scanner/view/internal/FilterView.kt | 142 ++++++++++++++++ .../ui/scanner/view/internal/ScanEmptyView.kt | 106 ++++++++++++ .../ui/scanner/view/internal/ScanErrorView.kt | 67 ++++++++ scanner/src/main/res/values/strings.xml | 79 +++++++++ settings.gradle.kts | 1 + 22 files changed, 1821 insertions(+) create mode 100644 scanner/build.gradle.kts create mode 100644 scanner/src/main/AndroidManifest.xml create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/HiltModule.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerScreen.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerScreenResult.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerView.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/di/ScannerHiltModule.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItem.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItems.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DevicesListView.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/viewmodel/ScannerViewModel.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/DevicesScanFilter.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/ScannerRepository.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/ScanningState.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceConnectingView.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceDisconnectedView.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/ScannerAppBar.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/FilterView.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanEmptyView.kt create mode 100644 scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanErrorView.kt create mode 100644 scanner/src/main/res/values/strings.xml diff --git a/scanner/build.gradle.kts b/scanner/build.gradle.kts new file mode 100644 index 00000000..209efd9a --- /dev/null +++ b/scanner/build.gradle.kts @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +plugins { + alias(libs.plugins.nordic.feature) + alias(libs.plugins.nordic.nexus.android) + alias(libs.plugins.kotlin.parcelize) +} + +group = "no.nordicsemi.android.common" + +nordicNexusPublishing { + POM_ARTIFACT_ID = "scanner" + POM_NAME = "Nordic Kotlin library for BLE server side." + + POM_DESCRIPTION = "Nordic Android Kotlin BLE library" + POM_URL = "https://github.com/NordicPlayground/Kotlin-BLE-Library" + POM_SCM_URL = "https://github.com/NordicPlayground/Kotlin-BLE-Library" + POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Kotlin-BLE-Library.git" + POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Kotlin-BLE-Library.git" +} + +android { + namespace = "no.nordicsemi.android.common.scanner" +} + +dependencies { + implementation(project(":theme")) + implementation(project(":core")) + implementation(project(":permissions-ble")) + + api(libs.nordic.blek.client.android) + + implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.material.iconsExtended) +} diff --git a/scanner/src/main/AndroidManifest.xml b/scanner/src/main/AndroidManifest.xml new file mode 100644 index 00000000..146a36a7 --- /dev/null +++ b/scanner/src/main/AndroidManifest.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/HiltModule.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/HiltModule.kt new file mode 100644 index 00000000..fca5e3f1 --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/HiltModule.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner + +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import no.nordicsemi.android.kotlin.ble.scanner.BleScanner + +@Module +@InstallIn(SingletonComponent::class) +internal class HiltModule { + + @Provides + fun providesScanner( + @ApplicationContext + context: Context + ): CentralManager { + return BleScanner(context) + } +} diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerScreen.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerScreen.kt new file mode 100644 index 00000000..89841d4f --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerScreen.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.android.kotlin.ble.ui.scanner + +import android.os.ParcelUuid +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.res.stringResource +import no.nordicsemi.android.kotlin.ble.ui.scanner.main.DeviceListItem +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.ScannerAppBar +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResults + +@Composable +fun ScannerScreen( + title: String = stringResource(id = R.string.scanner_screen), + uuid: ParcelUuid?, + cancellable: Boolean = true, + onResult: (ScannerScreenResult) -> Unit, + deviceItem: @Composable (BleScanResults) -> Unit = { + DeviceListItem(it.advertisedName ?: it.device.name, it.device.address) + } +) { + var isScanning by rememberSaveable { mutableStateOf(false) } + + Column { + if (cancellable) { + ScannerAppBar(title, isScanning) { onResult(ScanningCancelled) } + } else { + ScannerAppBar(title, isScanning) + } + ScannerView( + uuid = uuid, + onScanningStateChanged = { isScanning = it }, + onResult = { onResult(DeviceSelected(it)) }, + deviceItem = deviceItem, + ) + } +} diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerScreenResult.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerScreenResult.kt new file mode 100644 index 00000000..f2887458 --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerScreenResult.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner + +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResults + +sealed interface ScannerScreenResult + +data object ScanningCancelled : ScannerScreenResult + +data class DeviceSelected(val scanResults: BleScanResults) : ScannerScreenResult diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerView.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerView.kt new file mode 100644 index 00000000..ebbea08d --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/ScannerView.kt @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner + +import android.os.ParcelUuid +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.pulltorefresh.PullToRefreshContainer +import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import no.nordicsemi.android.common.permissions.ble.RequireBluetooth +import no.nordicsemi.android.common.permissions.ble.RequireLocation +import no.nordicsemi.android.common.theme.R +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResults +import no.nordicsemi.android.kotlin.ble.ui.scanner.main.DeviceListItem +import no.nordicsemi.android.kotlin.ble.ui.scanner.main.DevicesListView +import no.nordicsemi.android.kotlin.ble.ui.scanner.main.viewmodel.ScannerViewModel +import no.nordicsemi.android.kotlin.ble.ui.scanner.repository.ScanningState +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.internal.FilterView + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ScannerView( + uuid: ParcelUuid?, + onScanningStateChanged: (Boolean) -> Unit = {}, + onResult: (BleScanResults) -> Unit, + deviceItem: @Composable (BleScanResults) -> Unit = { + DeviceListItem(it.advertisedName ?: it.device.name, it.device.address) + }, + showFilter: Boolean = true +) { + RequireBluetooth( + onChanged = { onScanningStateChanged(it) } + ) { + RequireLocation( + onChanged = { onScanningStateChanged(it) } + ) { isLocationRequiredAndDisabled -> + val viewModel = hiltViewModel() + .apply { setFilterUuid(uuid) } + + val state by viewModel.state.collectAsStateWithLifecycle(ScanningState.Loading) + val config by viewModel.filterConfig.collectAsStateWithLifecycle() + + Column(modifier = Modifier.fillMaxSize()) { + if (showFilter) + FilterView( + config = config, + onChanged = { viewModel.setFilter(it) }, + modifier = Modifier + .fillMaxWidth() + .background(colorResource(id = R.color.appBarColor)) + .padding(horizontal = 16.dp), + ) + + val pullRefreshState = rememberPullToRefreshState() + if (pullRefreshState.isRefreshing) { + LaunchedEffect(true) { + viewModel.refresh() + pullRefreshState.endRefresh() + } + } + + Box( + modifier = Modifier + .nestedScroll(pullRefreshState.nestedScrollConnection) + .clipToBounds() + ) { + if (!pullRefreshState.isRefreshing) { + DevicesListView( + isLocationRequiredAndDisabled = isLocationRequiredAndDisabled, + state = state, + modifier = Modifier.fillMaxSize(), + onClick = { onResult(it) }, + deviceItem = deviceItem, + ) + } + + PullToRefreshContainer( + modifier = Modifier.align(Alignment.TopCenter), + state = pullRefreshState, + ) + } + } + } + } +} diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/di/ScannerHiltModule.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/di/ScannerHiltModule.kt new file mode 100644 index 00000000..2919f711 --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/di/ScannerHiltModule.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.di + +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothManager +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent + +@Suppress("unused") +@Module +@InstallIn(SingletonComponent::class) +internal class ScannerHiltModule { + + @Provides + fun provideBluetoothAdapter( + @ApplicationContext context: Context + ): BluetoothAdapter { + val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager + return manager.adapter + } +} \ No newline at end of file diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItem.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItem.kt new file mode 100644 index 00000000..5be06d0e --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItem.kt @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.main + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Bluetooth +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.common.theme.NordicTheme +import no.nordicsemi.android.common.theme.view.CircularIcon +import no.nordicsemi.android.common.theme.view.RssiIcon +import no.nordicsemi.android.kotlin.ble.ui.scanner.R + +@Composable +fun DeviceListItem( + name: String?, + address: String, + modifier: Modifier = Modifier, + extras: @Composable () -> Unit = {}, +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically) + { + CircularIcon(Icons.Default.Bluetooth) + + Spacer(modifier = Modifier.width(16.dp)) + + Column( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + name?.takeIf { it.isNotEmpty() }?.let { name -> + Text( + text = name, + style = MaterialTheme.typography.titleMedium + ) + } ?: Text( + text = stringResource(id = R.string.device_no_name), + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.alpha(0.7f) + ) + Text( + text = address, + style = MaterialTheme.typography.bodyMedium + ) + } + + extras() + } +} + +@Preview +@Composable +private fun DeviceListItemPreview() { + NordicTheme { + DeviceListItem( + name = "Device name", + address = "AA:BB:CC:DD:EE:FF", + extras = { + RssiIcon(rssi = -45) + } + ) + } +} \ No newline at end of file diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItems.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItems.kt new file mode 100644 index 00000000..6e88b3b0 --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItems.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.main + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.kotlin.ble.ui.scanner.repository.ScanningState +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResults +import no.nordicsemi.android.kotlin.ble.ui.scanner.R + +@Suppress("FunctionName") +fun LazyListScope.DeviceListItems( + devices: ScanningState.DevicesDiscovered, + onClick: (BleScanResults) -> Unit, + deviceView: @Composable (BleScanResults) -> Unit, +) { + val bondedDevices = devices.bonded + val discoveredDevices = devices.notBonded + + if (bondedDevices.isNotEmpty()) { + item { + Text( + text = stringResource(id = R.string.bonded_devices), + style = MaterialTheme.typography.titleSmall, + modifier = Modifier.padding(start = 8.dp, bottom = 8.dp) + ) + } + items(bondedDevices.size) { + ClickableDeviceItem(bondedDevices[it], onClick, deviceView) + } + } + + if (discoveredDevices.isNotEmpty()) { + item { + Text( + text = stringResource(id = R.string.discovered_devices), + style = MaterialTheme.typography.titleSmall, + modifier = Modifier.padding(start = 8.dp, bottom = 8.dp) + ) + } + + items(discoveredDevices.size) { + ClickableDeviceItem(discoveredDevices[it], onClick, deviceView) + } + } +} + +@Composable +private fun ClickableDeviceItem( + device: BleScanResults, + onClick: (BleScanResults) -> Unit, + deviceView: @Composable (BleScanResults) -> Unit, +) { + Box(modifier = Modifier + .clip(RoundedCornerShape(10.dp)) + .clickable { onClick(device) } + .padding(8.dp) + ) { + deviceView(device) + } +} diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DevicesListView.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DevicesListView.kt new file mode 100644 index 00000000..e3ae3fa5 --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DevicesListView.kt @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.main + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.common.theme.NordicTheme +import no.nordicsemi.android.kotlin.ble.ui.scanner.repository.ScanningState +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.internal.ScanEmptyView +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.internal.ScanErrorView +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResults + +@Composable +fun DevicesListView( + isLocationRequiredAndDisabled: Boolean, + state: ScanningState, + onClick: (BleScanResults) -> Unit, + modifier: Modifier = Modifier, + deviceItem: @Composable (BleScanResults) -> Unit = { + DeviceListItem(it.device.name, it.device.address) + }, +) { + LazyColumn( + modifier = modifier, + contentPadding = PaddingValues(horizontal = 8.dp, vertical = 16.dp) + ) { + when (state) { + is ScanningState.Loading -> item { ScanEmptyView(isLocationRequiredAndDisabled) } + is ScanningState.DevicesDiscovered -> { + if (state.isEmpty()) { + item { ScanEmptyView(isLocationRequiredAndDisabled) } + } else { + DeviceListItems(state, onClick, deviceItem) + } + } + is ScanningState.Error -> item { ScanErrorView(state.errorCode) } + } + } +} + +@Preview(name = "Location required") +@Composable +private fun DeviceListView_Preview_LocationRequired() { + NordicTheme { + DevicesListView( + isLocationRequiredAndDisabled = true, + state = ScanningState.Loading, + onClick = {} + ) + } +} + +@Preview +@Composable +private fun DeviceListView_Preview_LocationNotRequired() { + NordicTheme { + DevicesListView( + isLocationRequiredAndDisabled = false, + state = ScanningState.Loading, + onClick = {} + ) + } +} + +@Preview +@Composable +private fun DeviceListView_Preview_Error() { + NordicTheme { + DevicesListView( + isLocationRequiredAndDisabled = true, + state = ScanningState.Error(1), + onClick = {} + ) + } +} + +@Preview +@Composable +private fun DeviceListView_Preview_Empty() { + NordicTheme { + DevicesListView( + isLocationRequiredAndDisabled = true, + state = ScanningState.DevicesDiscovered(emptyList()), + onClick = {} + ) + } +} diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/viewmodel/ScannerViewModel.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/viewmodel/ScannerViewModel.kt new file mode 100644 index 00000000..2f2b9a19 --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/viewmodel/ScannerViewModel.kt @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.main.viewmodel + +import android.os.ParcelUuid +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.cancellable +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResults +import no.nordicsemi.android.kotlin.ble.scanner.aggregator.BleScanResultAggregator +import no.nordicsemi.android.kotlin.ble.scanner.errors.ScanFailedError +import no.nordicsemi.android.kotlin.ble.scanner.errors.ScanningFailedException +import no.nordicsemi.android.kotlin.ble.ui.scanner.repository.DevicesScanFilter +import no.nordicsemi.android.kotlin.ble.ui.scanner.repository.ScannerRepository +import no.nordicsemi.android.kotlin.ble.ui.scanner.repository.ScanningState +import javax.inject.Inject + +private const val FILTER_RSSI = -50 // [dBm] + +@HiltViewModel +internal class ScannerViewModel @Inject constructor( + private val scannerRepository: ScannerRepository, +) : ViewModel() { + private var uuid: ParcelUuid? = null + + val filterConfig = MutableStateFlow( + DevicesScanFilter( + filterUuidRequired = true, + filterNearbyOnly = false, + filterWithNames = true + ) + ) + + private var currentJob: Job? = null + + private val _state = MutableStateFlow(ScanningState.Loading) + val state = _state.asStateFlow() + + init { + relaunchScanning() + } + + private fun relaunchScanning() { + currentJob?.cancel() + val aggregator = BleScanResultAggregator() + currentJob = scannerRepository.getScannerState() + .map { aggregator.aggregate(it) } + .filter { it.isNotEmpty() } + .combine(filterConfig) { result, config -> + result.applyFilters(config) + } + .onStart { _state.value = ScanningState.Loading } + .cancellable() + .onEach { + _state.value = ScanningState.DevicesDiscovered(it) + } + .catch { e -> + _state.value = (e as? ScanningFailedException)?.let { + ScanningState.Error(it.errorCode.value) + } ?: ScanningState.Error(ScanFailedError.UNKNOWN.value) + } + .launchIn(viewModelScope) + } + + // This can't be observed in View Model Scope, as it can exist even when the + // scanner is not visible. Scanner state stops scanning when it is not observed. + // .stateIn(viewModelScope, SharingStarted.Lazily, ScanningState.Loading) + private fun List.applyFilters(config: DevicesScanFilter) = + filter { + uuid == null || + config.filterUuidRequired == false || + it.lastScanResult?.scanRecord?.serviceUuids?.contains(uuid) == true + } + .filter { !config.filterNearbyOnly || it.highestRssi >= FILTER_RSSI } + .filter { !config.filterWithNames || it.device.hasName || it.advertisedName?.isNotEmpty() == true } + + fun setFilterUuid(uuid: ParcelUuid?) { + this.uuid = uuid + if (uuid == null) { + filterConfig.value = filterConfig.value.copy(filterUuidRequired = null) + } + } + + fun setFilter(config: DevicesScanFilter) { + this.filterConfig.value = config + } + + fun refresh() { + relaunchScanning() + } +} diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/DevicesScanFilter.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/DevicesScanFilter.kt new file mode 100644 index 00000000..946a45dc --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/DevicesScanFilter.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.repository + +internal data class DevicesScanFilter( + val filterUuidRequired: Boolean?, + val filterNearbyOnly: Boolean, + val filterWithNames: Boolean +) diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/ScannerRepository.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/ScannerRepository.kt new file mode 100644 index 00000000..cfbc573a --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/ScannerRepository.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.repository + +import android.annotation.SuppressLint +import dagger.hilt.android.scopes.ViewModelScoped +import no.nordicsemi.android.kotlin.ble.scanner.BleScanner +import javax.inject.Inject + +@ViewModelScoped +class ScannerRepository @Inject internal constructor( + private val nordicScanner: BleScanner +) { + + @SuppressLint("MissingPermission") + fun getScannerState() = nordicScanner.scan() +} diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/ScanningState.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/ScanningState.kt new file mode 100644 index 00000000..e25abce4 --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/repository/ScanningState.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.repository + +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResults + +sealed class ScanningState { + + data object Loading : ScanningState() + + data class Error(val errorCode: Int) : ScanningState() + + data class DevicesDiscovered(val devices: List) : ScanningState() { + val bonded: List = devices.filter { it.device.isBonded } + + val notBonded: List = devices.filter { !it.device.isBonded } + + fun size(): Int = bonded.size + notBonded.size + + fun isEmpty(): Boolean = devices.isEmpty() + } + + fun isRunning(): Boolean { + return this is Loading || this is DevicesDiscovered + } +} diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceConnectingView.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceConnectingView.kt new file mode 100644 index 00000000..a5a7476d --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceConnectingView.kt @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.view + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.HourglassTop +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.common.theme.NordicTheme +import no.nordicsemi.android.common.theme.view.CircularIcon +import no.nordicsemi.android.kotlin.ble.ui.scanner.R + +@Composable +fun DeviceConnectingView( + modifier: Modifier = Modifier, + content: @Composable ColumnScope.(PaddingValues) -> Unit = {} +) { + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally + ) { + OutlinedCard( + modifier = Modifier + .widthIn(max = 460.dp), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + CircularIcon(imageVector = Icons.Default.HourglassTop) + + Text( + text = stringResource(id = R.string.device_connecting), + style = MaterialTheme.typography.titleMedium + ) + + Text( + text = stringResource(id = R.string.device_explanation), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodyMedium + ) + + Text( + text = stringResource(id = R.string.device_please_wait), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleLarge + ) + } + } + + content(PaddingValues(top = 16.dp)) + } +} + +@Preview +@Composable +private fun DeviceConnectingView_Preview() { + NordicTheme { + DeviceConnectingView { padding -> + Button( + onClick = {}, + modifier = Modifier.padding(padding) + ) { + Text(text = "Cancel") + } + } + } +} diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceDisconnectedView.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceDisconnectedView.kt new file mode 100644 index 00000000..f0695aca --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceDisconnectedView.kt @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.view + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.HighlightOff +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.common.theme.NordicTheme +import no.nordicsemi.android.common.theme.view.CircularIcon +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus +import no.nordicsemi.android.kotlin.ble.ui.scanner.R + +enum class Reason { + USER, UNKNOWN, LINK_LOSS, MISSING_SERVICE +} + +@Composable +fun DeviceDisconnectedView( + status: BleGattConnectionStatus, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.(PaddingValues) -> Unit = {}, +) { + val disconnectedReason = when (status) { + BleGattConnectionStatus.UNKNOWN -> stringResource(id = R.string.device_reason_unknown) + BleGattConnectionStatus.SUCCESS -> stringResource(id = R.string.device_reason_user) + BleGattConnectionStatus.TERMINATE_LOCAL_HOST -> stringResource(id = R.string.device_reason_terminate_local_host) + BleGattConnectionStatus.TERMINATE_PEER_USER -> stringResource(id = R.string.device_reason_terminate_peer_user) + BleGattConnectionStatus.LINK_LOSS -> stringResource(id = R.string.device_reason_link_loss) + BleGattConnectionStatus.NOT_SUPPORTED -> stringResource(id = R.string.device_reason_missing_service) + BleGattConnectionStatus.CANCELLED -> stringResource(id = R.string.device_reason_cancelled) + BleGattConnectionStatus.TIMEOUT -> stringResource(id = R.string.device_reason_timeout) + } + + DeviceDisconnectedView(disconnectedReason = disconnectedReason, modifier, content) +} + +@Composable +fun DeviceDisconnectedView( + reason: Reason, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.(PaddingValues) -> Unit = {}, +) { + val disconnectedReason = when (reason) { + Reason.USER -> stringResource(id = R.string.device_reason_user) + Reason.LINK_LOSS -> stringResource(id = R.string.device_reason_link_loss) + Reason.MISSING_SERVICE -> stringResource(id = R.string.device_reason_missing_service) + Reason.UNKNOWN -> stringResource(id = R.string.device_reason_unknown) + } + + DeviceDisconnectedView(disconnectedReason = disconnectedReason, modifier, content) +} + +@Composable +fun DeviceDisconnectedView( + disconnectedReason: String, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.(PaddingValues) -> Unit = {}, +) { + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally + ) { + OutlinedCard( + modifier = Modifier + .widthIn(max = 460.dp), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + CircularIcon(imageVector = Icons.Default.HighlightOff) + + Text( + text = stringResource(id = R.string.device_disconnected), + style = MaterialTheme.typography.titleMedium + ) + + Text( + text = disconnectedReason, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodyMedium + ) + } + } + + content(PaddingValues(top = 16.dp)) + } +} + +@Preview +@Composable +private fun DeviceDisconnectedView_Preview() { + NordicTheme { + DeviceDisconnectedView( + reason = Reason.MISSING_SERVICE, + content = { padding -> + Button( + onClick = {}, + modifier = Modifier.padding(padding) + ) { + Text(text = "Retry") + } + } + ) + } +} diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/ScannerAppBar.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/ScannerAppBar.kt new file mode 100644 index 00000000..51d98cbe --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/ScannerAppBar.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.view + +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.common.theme.view.NordicAppBar + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ScannerAppBar( + text: String, + showProgress: Boolean = false, + backButtonIcon: ImageVector = Icons.AutoMirrored.Filled.ArrowBack, + onNavigationButtonClick: (() -> Unit)? = null, +) { + NordicAppBar( + text = text, + backButtonIcon = backButtonIcon, + onNavigationButtonClick = onNavigationButtonClick, + actions = { + if (showProgress) { + CircularProgressIndicator( + modifier = Modifier.padding(horizontal = 16.dp).size(30.dp), + color = MaterialTheme.colorScheme.onPrimary + ) + } + }, + ) +} \ No newline at end of file diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/FilterView.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/FilterView.kt new file mode 100644 index 00000000..e05cf581 --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/FilterView.kt @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.view.internal + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.Label +import androidx.compose.material.icons.filled.Done +import androidx.compose.material.icons.filled.Widgets +import androidx.compose.material.icons.filled.Wifi +import androidx.compose.material3.ElevatedFilterChip +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.common.theme.NordicTheme +import no.nordicsemi.android.kotlin.ble.ui.scanner.R +import no.nordicsemi.android.kotlin.ble.ui.scanner.repository.DevicesScanFilter + +@Composable +internal fun FilterView( + config: DevicesScanFilter, + onChanged: (DevicesScanFilter) -> Unit, + modifier: Modifier = Modifier, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + modifier = modifier, + ) { + config.filterUuidRequired?.let { + ElevatedFilterChip( + selected = !it, + onClick = { onChanged(config.copy(filterUuidRequired = !it)) }, + label = { Text(text = stringResource(id = R.string.filter_uuid),) }, + modifier = Modifier.padding(end = 8.dp), + leadingIcon = { + if (!it) { + Icon(Icons.Default.Done, contentDescription = "") + } else { + Icon(Icons.Default.Widgets, contentDescription = "") + } + }, + ) + } + config.filterNearbyOnly.let { + ElevatedFilterChip( + selected = it, + onClick = { onChanged(config.copy(filterNearbyOnly = !it)) }, + label = { Text(text = stringResource(id = R.string.filter_nearby),) }, + modifier = Modifier.padding(end = 8.dp), + leadingIcon = { + if (it) { + Icon(Icons.Default.Done, contentDescription = "") + } else { + Icon(Icons.Default.Wifi, contentDescription = "") + } + }, + ) + } + config.filterWithNames.let { + ElevatedFilterChip( + selected = it, + onClick = { onChanged(config.copy(filterWithNames = !it)) }, + label = { Text(text = stringResource(id = R.string.filter_name),) }, + modifier = Modifier.padding(end = 8.dp), + leadingIcon = { + if (it) { + Icon(Icons.Default.Done, contentDescription = "") + } else { + Icon(Icons.AutoMirrored.Filled.Label, contentDescription = "") + } + }, + ) + } + } +} + +@Preview +@Composable +private fun FilterViewPreview() { + NordicTheme { + Column { + FilterView( + config = DevicesScanFilter( + filterUuidRequired = true, + filterNearbyOnly = true, + filterWithNames = true, + ), + onChanged = {}, + modifier = Modifier.fillMaxWidth(), + ) + + FilterView( + config = DevicesScanFilter( + filterUuidRequired = false, + filterNearbyOnly = false, + filterWithNames = false, + ), + onChanged = {}, + modifier = Modifier.fillMaxWidth(), + ) + } + } +} \ No newline at end of file diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanEmptyView.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanEmptyView.kt new file mode 100644 index 00000000..bc39a5f4 --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanEmptyView.kt @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.view.internal + +import android.content.Context +import android.content.Intent +import android.provider.Settings +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.BluetoothSearching +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Devices +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.common.core.parseBold +import no.nordicsemi.android.common.theme.NordicTheme +import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.kotlin.ble.ui.scanner.R + +@Composable +internal fun ScanEmptyView( + requireLocation: Boolean, +) { + WarningView( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + imageVector = Icons.AutoMirrored.Filled.BluetoothSearching, + title = stringResource(id = R.string.no_device_guide_title), + hint = stringResource(id = R.string.no_device_guide_info) + if (requireLocation) { + "\n\n" + stringResource(id = R.string.no_device_guide_location_info) + } else { + "" + }.parseBold(), + hintTextAlign = TextAlign.Justify, + ) { + if (requireLocation) { + val context = LocalContext.current + Button(onClick = { openLocationSettings(context) }) { + Text(text = stringResource(id = R.string.action_location_settings)) + } + } + } +} + +private fun openLocationSettings(context: Context) { + val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(intent) +} + +@Preview +@Composable +private fun ScanEmptyViewPreview_RequiredLocation() { + NordicTheme { + ScanEmptyView( + requireLocation = true, + ) + } +} + +@Preview(device = Devices.TABLET) +@Composable +private fun ScanEmptyViewPreview() { + NordicTheme { + ScanEmptyView( + requireLocation = false, + ) + } +} \ No newline at end of file diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanErrorView.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanErrorView.kt new file mode 100644 index 00000000..a56c12b2 --- /dev/null +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanErrorView.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.kotlin.ble.ui.scanner.view.internal + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.BluetoothSearching +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.common.theme.NordicTheme +import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.kotlin.ble.ui.scanner.R + +@Composable +internal fun ScanErrorView( + error: Int, +) { + WarningView( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + imageVector = Icons.AutoMirrored.Filled.BluetoothSearching, + title = stringResource(id = R.string.scanner_error), + hint = stringResource(id = R.string.scan_failed, error), + ) +} + +@Preview +@Composable +private fun ErrorSectionPreview() { + NordicTheme { + ScanErrorView(3) + } +} \ No newline at end of file diff --git a/scanner/src/main/res/values/strings.xml b/scanner/src/main/res/values/strings.xml new file mode 100644 index 00000000..fc13b0ca --- /dev/null +++ b/scanner/src/main/res/values/strings.xml @@ -0,0 +1,79 @@ + + + + + Bluetooth + Location + + Filters + All + Nearby + Name + + No name + Bonded devices + Discovered devices + + Scanning failed with error %d. + Scanner + Scanning failed + Location settings + Back + + CAN\'T SEE YOUR DEVICE? + 1. Make sure the device is turned on and is connected to + a power source.\n\n2. Make sure the appropriate firmware and SoftDevice are flashed. + 3. Location is turned off. Most Android phones + require it in order to scan for Bluetooth LE devices. If you are sure your + device is advertising and it doesn\'t show up here, click the button below to + enable Location. + + App bar navigation back icon. + + Bonding in progress. Please follow instruction on the screen. + Bonding success. Please wait for the redirect to chosen profile screen. + We cannot get data from the peripheral without bonding. Please bond the device. + + Disconnected + Device disconnected successfully. + Device disconnected with unknown reason. + Device signal has been lost. + Device was disconnected, because required services are missing. + The local device initiated disconnection. + The remote device initiated graceful disconnection. + Connection attempt was cancelled. + The connection timed out. + + Connecting… + The mobile is trying to connect to peripheral device. + Please wait… + diff --git a/settings.gradle.kts b/settings.gradle.kts index 95f89769..389229d3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -55,6 +55,7 @@ include(":core") include(":theme") include(":ui") include(":logger") +//include(":scanner") include(":navigation") include(":analytics") include(":permissions-ble") From f8ea8654dc60ef503442d2ddb17363c024c80b48 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 12 Jun 2024 13:27:37 +0200 Subject: [PATCH 13/27] Fixed edge to edge --- .../java/no/nordicsemi/android/common/test/MainActivity.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt b/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt index 8af93321..a6b96602 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt @@ -32,8 +32,10 @@ package no.nordicsemi.android.common.test import android.annotation.SuppressLint +import android.graphics.Color import android.os.Bundle import android.widget.Toast +import androidx.activity.SystemBarStyle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.fillMaxHeight @@ -111,8 +113,9 @@ class MainActivity : NordicActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setDecorFitsSystemWindows(false) - enableEdgeToEdge() // <- This is the Way. + enableEdgeToEdge( + statusBarStyle = SystemBarStyle.dark(Color.TRANSPARENT) + ) val menuItems = listOf( Item("Main", Tabs, Icons.Filled.Verified), From c54c48ed602ad01f0e087870f1cf467864d60c97 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Tue, 18 Jun 2024 14:29:35 +0200 Subject: [PATCH 14/27] Migration of permissions:wifi to new ui module --- permissions-wifi/build.gradle.kts | 14 ++++++-------- .../wifi/{wifi => state}/WifiStateManager.kt | 2 +- .../permissions/wifi/view/LocationDisabledView.kt | 6 +++--- .../wifi/view/LocationPermissionRequiredView.kt | 6 +++--- .../permissions/wifi/view/WifiDisabledView.kt | 6 +++--- .../permissions/wifi/view/WifiNotAvailableView.kt | 6 +++--- .../wifi/view/WifiPermissionRequiredView.kt | 6 +++--- .../wifi/viewmodel/PermissionViewModel.kt | 2 +- 8 files changed, 23 insertions(+), 25 deletions(-) rename permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/{wifi => state}/WifiStateManager.kt (98%) diff --git a/permissions-wifi/build.gradle.kts b/permissions-wifi/build.gradle.kts index 7793446a..23bb9887 100644 --- a/permissions-wifi/build.gradle.kts +++ b/permissions-wifi/build.gradle.kts @@ -1,23 +1,19 @@ plugins { alias(libs.plugins.nordic.library.compose) alias(libs.plugins.nordic.hilt) - alias(libs.plugins.nordic.nexus) + alias(libs.plugins.nordic.nexus.android) } group = "no.nordicsemi.android.common" nordicNexusPublishing { POM_ARTIFACT_ID = "permissions-wifi" - POM_NAME = "Nordic library for checking permissions required for scanning for Wi-Fi networks." + POM_NAME = "Nordic library for checking Wi-Fi state and permissions required for scanning for Wi-Fi networks." POM_DESCRIPTION = "Nordic Android Common Libraries" POM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" POM_SCM_URL = "https://github.com/NordicPlayground/Android-Common-Libraries" POM_SCM_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" POM_SCM_DEV_CONNECTION = "scm:git@github.com:NordicPlayground/Android-Common-Libraries.git" - - POM_DEVELOPER_ID = "hiar" - POM_DEVELOPER_NAME = "Himali Aryal" - POM_DEVELOPER_EMAIL = "himali.aryal@nordicsemi.no" } android { @@ -25,9 +21,11 @@ android { } dependencies { + implementation(project(":ui")) + + implementation(libs.androidx.compose.material.iconsExtended) + implementation(libs.androidx.navigation.compose) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.lifecycle.runtime.compose) - implementation(libs.androidx.compose.material.iconsExtended) - implementation(project(":theme")) } \ No newline at end of file diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/wifi/WifiStateManager.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/state/WifiStateManager.kt similarity index 98% rename from permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/wifi/WifiStateManager.kt rename to permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/state/WifiStateManager.kt index 46bfa6b5..c7baf083 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/wifi/WifiStateManager.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/state/WifiStateManager.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.common.permissions.wifi.wifi +package no.nordicsemi.android.common.permissions.wifi.state import android.annotation.SuppressLint import android.content.BroadcastReceiver diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt index f6da4973..bb028b38 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt @@ -40,6 +40,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.LocationOff import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -47,8 +48,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.common.permissions.wifi.R -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView @Composable internal fun LocationDisabledView() { @@ -74,7 +74,7 @@ private fun enableLocation(context: Context) { @Preview @Composable private fun LocationDisabledViewPreview() { - NordicTheme { + MaterialTheme { LocationDisabledView() } } \ No newline at end of file diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt index ce28bef8..77d848c3 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt @@ -44,6 +44,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.LocationOff import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -58,8 +59,7 @@ import androidx.core.content.ContextCompat import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.permissions.wifi.R import no.nordicsemi.android.common.permissions.wifi.viewmodel.PermissionViewModel -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView @Composable internal fun LocationPermissionRequiredView() { @@ -127,7 +127,7 @@ private fun openPermissionSettings(context: Context) { @Preview @Composable private fun LocationPermissionRequiredView_Preview() { - NordicTheme { + MaterialTheme { LocationPermissionRequiredView( permissionDenied = false, onGrantClicked = { }, diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt index 70af4131..c31a32e4 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt @@ -40,6 +40,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.WifiOff import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -47,8 +48,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.common.permissions.wifi.R -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView @Composable internal fun WifiDisabledView() { @@ -74,7 +74,7 @@ private fun enableWifi(context: Context) { @Preview @Composable private fun WifiDisabledViewPreview() { - NordicTheme { + MaterialTheme { WifiDisabledView() } } \ No newline at end of file diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt index 0151f7c7..2b451b52 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt @@ -36,13 +36,13 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.WifiOff +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.common.permissions.wifi.R -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView @Composable internal fun WifiNotAvailableView() { @@ -59,7 +59,7 @@ internal fun WifiNotAvailableView() { @Preview @Composable private fun WifiNotAvailableView_Preview() { - NordicTheme { + MaterialTheme { WifiNotAvailableView() } } diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt index 40af6952..a2ced540 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt @@ -46,6 +46,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.WifiOff import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -60,8 +61,7 @@ import androidx.core.content.ContextCompat import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.permissions.wifi.R import no.nordicsemi.android.common.permissions.wifi.viewmodel.PermissionViewModel -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView @Composable internal fun WifiPermissionRequiredView() { @@ -123,7 +123,7 @@ private fun openPermissionSettings(context: Context) { @Preview @Composable private fun WifiPermissionRequiredViewPreview() { - NordicTheme { + MaterialTheme { WifiPermissionRequiredView() } } \ No newline at end of file diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/viewmodel/PermissionViewModel.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/viewmodel/PermissionViewModel.kt index 7c7b81fa..6420a064 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/viewmodel/PermissionViewModel.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/viewmodel/PermissionViewModel.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.flow.stateIn import no.nordicsemi.android.common.permissions.wifi.location.LocationStateManager import no.nordicsemi.android.common.permissions.wifi.utils.WifiPermissionNotAvailableReason import no.nordicsemi.android.common.permissions.wifi.utils.WifiPermissionState -import no.nordicsemi.android.common.permissions.wifi.wifi.WifiStateManager +import no.nordicsemi.android.common.permissions.wifi.state.WifiStateManager import javax.inject.Inject @HiltViewModel From ae7f666fdbc907f1e9224cada090e9c4462fc998 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Tue, 18 Jun 2024 14:59:36 +0200 Subject: [PATCH 15/27] Unused module removed --- settings.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 389229d3..a1e0c1cd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -62,4 +62,3 @@ include(":permissions-ble") include(":permissions-internet") include(":permissions-nfc") include(":permissions-wifi") -include(":data") From 3d90762824f421f5aff3d9fa761cae639f9a6dae Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Tue, 18 Jun 2024 15:00:00 +0200 Subject: [PATCH 16/27] Minor fix in NordicLargeAppBar --- .../no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt index a84fd513..c80c3fa5 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt @@ -29,8 +29,6 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -@file:OptIn(ExperimentalMaterial3Api::class) - package no.nordicsemi.android.common.ui.view import androidx.compose.foundation.layout.RowScope @@ -43,7 +41,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable @@ -54,6 +51,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp import no.nordicsemi.android.common.ui.R +@ExperimentalMaterial3Api @Composable fun NordicLargeAppBar( title: @Composable () -> Unit, From fe70c940c4d7db08463bd5e099d1f78bcd925d90 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Tue, 18 Jun 2024 16:34:58 +0200 Subject: [PATCH 17/27] UP previews --- .../ble/view/BluetoothDisabledView.kt | 2 +- .../ble/view/BluetoothNotAvailableView.kt | 2 +- .../view/LocationPermissionRequiredView.kt | 2 +- .../internet/view/InternetNotAvailableView.kt | 2 +- .../permissions/nfc/view/NfcDisabledView.kt | 2 +- .../nfc/view/NfcNotAvailableView.kt | 2 +- .../wifi/view/LocationDisabledView.kt | 2 +- .../view/LocationPermissionRequiredView.kt | 2 +- .../permissions/wifi/view/WifiDisabledView.kt | 2 +- .../wifi/view/WifiNotAvailableView.kt | 2 +- .../wifi/view/WifiPermissionRequiredView.kt | 3 +- settings.gradle.kts | 2 - ui/build.gradle.kts | 2 - .../android/common/ui/view/CircularIcon.kt | 13 ++ .../common/ui/view/FloatingActionMenu.kt | 51 +++++++ .../android/common/ui/view/MarkdownText.kt | 51 ------- .../android/common/ui/view/NordicAppBar.kt | 22 +++ .../common/ui/view/NordicLargeAppBar.kt | 25 ++++ .../common/ui/view/NordicMediumAppBar.kt | 25 ++++ .../android/common/ui/view/ProgressItem.kt | 6 +- .../common/ui/view/RadioButtonGroup.kt | 42 +++++- .../android/common/ui/view/RssiIcon.kt | 22 ++- .../android/common/ui/view/SectionTitle.kt | 14 ++ .../android/common/ui/view/VerticalDivider.kt | 25 ++++ .../android/common/ui/view/WarningView.kt | 104 ++++++++++---- .../common/ui/view/WizardStepComponent.kt | 134 +++++++++++++++++- ui/src/main/res/values/strings.xml | 2 +- 27 files changed, 461 insertions(+), 102 deletions(-) delete mode 100644 ui/src/main/java/no/nordicsemi/android/common/ui/view/MarkdownText.kt diff --git a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt index a10934cd..5808254f 100644 --- a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt +++ b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt @@ -73,7 +73,7 @@ private fun enableBluetooth(context: Context) { context.startActivity(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun BluetoothDisabledView_Preview() { MaterialTheme { diff --git a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt index 2b6a3fb9..070da2f7 100644 --- a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt +++ b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt @@ -56,7 +56,7 @@ internal fun BluetoothNotAvailableView() { ) } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun BluetoothNotAvailableView_Preview() { MaterialTheme { diff --git a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt index 12b22e09..bd71e9cd 100644 --- a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt +++ b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt @@ -125,7 +125,7 @@ private fun openPermissionSettings(context: Context) { ) } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun LocationPermissionRequiredView_Preview() { MaterialTheme { diff --git a/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt b/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt index 813b58f7..4d8593dd 100644 --- a/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt +++ b/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt @@ -56,7 +56,7 @@ internal fun InternetNotAvailableView() { ) } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun BluetoothNotAvailableView_Preview() { MaterialTheme { diff --git a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt index 07c4cc6d..ddab73a0 100644 --- a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt +++ b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt @@ -70,7 +70,7 @@ private fun enableNfc(context: Context) { context.startActivity(Intent(Settings.ACTION_NFC_SETTINGS)) } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun NfcDisabledView_Preview() { MaterialTheme { diff --git a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt index 47ba9298..78e13c7c 100644 --- a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt +++ b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt @@ -55,7 +55,7 @@ internal fun NfcNotAvailableView() { ) } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun NfcNotAvailableView_Preview() { MaterialTheme { diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt index bb028b38..81a888cb 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt @@ -71,7 +71,7 @@ private fun enableLocation(context: Context) { context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun LocationDisabledViewPreview() { MaterialTheme { diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt index 77d848c3..a3b9863c 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt @@ -124,7 +124,7 @@ private fun openPermissionSettings(context: Context) { ) } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun LocationPermissionRequiredView_Preview() { MaterialTheme { diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt index c31a32e4..8cfef27e 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt @@ -71,7 +71,7 @@ private fun enableWifi(context: Context) { context.startActivity(Intent(Settings.ACTION_WIFI_SETTINGS)) } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun WifiDisabledViewPreview() { MaterialTheme { diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt index 2b451b52..28d764de 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt @@ -56,7 +56,7 @@ internal fun WifiNotAvailableView() { ) } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun WifiNotAvailableView_Preview() { MaterialTheme { diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt index a2ced540..17ad9c05 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt @@ -119,8 +119,7 @@ private fun openPermissionSettings(context: Context) { ) } -@RequiresApi(Build.VERSION_CODES.TIRAMISU) -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun WifiPermissionRequiredViewPreview() { MaterialTheme { diff --git a/settings.gradle.kts b/settings.gradle.kts index a1e0c1cd..48d0633b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -45,7 +45,6 @@ dependencyResolutionManagement { google() mavenCentral() maven(url = "https://androidx.dev/storage/compose-compiler/repository/") - maven(url = "https://jitpack.io") } } rootProject.name = "Common Libraries" @@ -55,7 +54,6 @@ include(":core") include(":theme") include(":ui") include(":logger") -//include(":scanner") include(":navigation") include(":analytics") include(":permissions-ble") diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index af1679be..5b580955 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -54,6 +54,4 @@ android { dependencies { implementation(libs.androidx.compose.material.iconsExtended) - - implementation(libs.markdown) } diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/CircularIcon.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/CircularIcon.kt index a0bb0057..33e07276 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/CircularIcon.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/CircularIcon.kt @@ -35,6 +35,8 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Restore import androidx.compose.material3.MaterialTheme import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable @@ -44,6 +46,7 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @Composable @@ -87,4 +90,14 @@ fun CircularIcon( backgroundColor = backgroundColor, enabled = enabled ) +} + +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +private fun CircularIconPreview() { + MaterialTheme { + CircularIcon( + imageVector = Icons.Default.Restore + ) + } } \ No newline at end of file diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/FloatingActionMenu.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/FloatingActionMenu.kt index d5a15814..ec43b4df 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/FloatingActionMenu.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/FloatingActionMenu.kt @@ -46,10 +46,15 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Restore import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.FloatingActionButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -73,6 +78,7 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.onClick import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max @@ -245,4 +251,49 @@ private fun Scrim(onClose: () -> Unit, modifier: Modifier = Modifier) { } } ) +} + +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true, heightDp = 200) +@Composable +private fun FloatingActionMenuPreview() { + MaterialTheme { + Scaffold( + floatingActionButton = { + var showDialog by remember { mutableStateOf(false) } + + FloatingActionMenu( + onDismissRequest = { showDialog = false }, + expanded = showDialog, + menuContent = { + FloatingActionMenuItem( + label = "Option 1", + imageVector = Icons.Default.Add, + onClick = { /*TODO*/ } + ) + FloatingActionMenuItem( + label = "Option 2", + imageVector = Icons.Default.Restore, + onClick = { /*TODO*/ } + ) + } + ) { + ExtendedFloatingActionButton( + text = { Text(text = "Menu") }, + icon = { + Icon( + imageVector = Icons.Default.Add, + contentDescription = null, + ) + }, + // Open the dialog when the FAB is clicked. + onClick = { showDialog = true }, + // Collapse the FAB when the dialog is shown. + expanded = !showDialog + ) + } + } + ) { innerPadding -> + Box(modifier = Modifier.padding(innerPadding)) + } + } } \ No newline at end of file diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/MarkdownText.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/MarkdownText.kt deleted file mode 100644 index ae275eb3..00000000 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/MarkdownText.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2023, Nordic Semiconductor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package no.nordicsemi.android.common.ui.view - -import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.TextStyle -import dev.jeziellago.compose.markdowntext.MarkdownText - -@Composable -fun NordicText( - text: String, - modifier: Modifier = Modifier, - style: TextStyle = MaterialTheme.typography.bodyMedium.copy(color = LocalContentColor.current) -) { - MarkdownText( - markdown = text, - style = style, - modifier = modifier) -} diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicAppBar.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicAppBar.kt index 82b4764c..a076de36 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicAppBar.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicAppBar.kt @@ -35,7 +35,9 @@ import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Menu +import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -49,6 +51,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import no.nordicsemi.android.common.ui.R @@ -103,3 +106,22 @@ fun NordicAppBar( windowInsets = windowInsets, ) } + +@OptIn(ExperimentalMaterial3Api::class) +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +private fun NordicAppBarPreview() { + MaterialTheme { + NordicAppBar( + title = { Text(text = "Title") }, + actions = { + IconButton(onClick = {}) { + Icon(imageVector = Icons.Default.Add, contentDescription = "") + } + IconButton(onClick = {}) { + Icon(imageVector = Icons.Default.MoreVert, contentDescription = "") + } + } + ) + } +} diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt index c80c3fa5..54eba1a5 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt @@ -35,12 +35,15 @@ import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Menu +import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable @@ -48,6 +51,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import no.nordicsemi.android.common.ui.R @@ -104,3 +108,24 @@ fun NordicLargeAppBar( windowInsets = windowInsets, ) } + +@OptIn(ExperimentalMaterial3Api::class) +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +private fun NordicLargeAppBarPreview() { + MaterialTheme { + NordicLargeAppBar( + title = { Text(text = "Title") }, + actions = { + IconButton(onClick = {}) { + Icon(imageVector = Icons.Default.Add, contentDescription = "") + } + IconButton(onClick = {}) { + Icon(imageVector = Icons.Default.MoreVert, contentDescription = "") + } + }, + onHamburgerButtonClick = {}, + showHamburgerButton = true, + ) + } +} diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicMediumAppBar.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicMediumAppBar.kt index 85debdc2..2ea5ed2c 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicMediumAppBar.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicMediumAppBar.kt @@ -35,18 +35,22 @@ import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Menu +import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MediumTopAppBar +import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import no.nordicsemi.android.common.ui.R @@ -101,3 +105,24 @@ fun NordicMediumAppBar( expandedHeight = expandedHeight, ) } + +@OptIn(ExperimentalMaterial3Api::class) +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +private fun NordicMediumAppBarPreview() { + MaterialTheme { + NordicMediumAppBar( + title = { Text(text = "Title") }, + actions = { + IconButton(onClick = {}) { + Icon(imageVector = Icons.Default.Add, contentDescription = "") + } + IconButton(onClick = {}) { + Icon(imageVector = Icons.Default.MoreVert, contentDescription = "") + } + }, + onHamburgerButtonClick = {}, + showHamburgerButton = true, + ) + } +} diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt index 4e531604..752af179 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt @@ -143,7 +143,7 @@ private fun ProgressItemPreview_Working() { } } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun ProgressItemPreview_Success() { MaterialTheme { @@ -154,7 +154,7 @@ private fun ProgressItemPreview_Success() { } } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun ProgressItemPreview_Disabled() { MaterialTheme { @@ -165,7 +165,7 @@ private fun ProgressItemPreview_Disabled() { } } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun ProgressItemPreview_Error() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/RadioButtonGroup.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RadioButtonGroup.kt index 91ba4c21..915bb6cc 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/RadioButtonGroup.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RadioButtonGroup.kt @@ -41,6 +41,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview data class RadioGroupViewEntity( val items: List, @@ -52,7 +53,10 @@ data class RadioButtonItem( ) @Composable -fun RadioButtonGroup(viewEntity: RadioGroupViewEntity, onItemClick: (RadioButtonItem) -> Unit) { +fun RadioButtonGroup( + viewEntity: RadioGroupViewEntity, + onItemClick: (RadioButtonItem) -> Unit +) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly @@ -83,3 +87,39 @@ fun HorizontalLabelRadioButtonGroup( } } } + +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +private fun RadioButtonGroupPreview() { + MaterialTheme { + RadioButtonGroup( + viewEntity = RadioGroupViewEntity( + listOf( + RadioButtonItem(label = "Option 1", isChecked = true), + RadioButtonItem(label = "Option 2", isChecked = false), + RadioButtonItem(label = "Option 3", isChecked = false), + RadioButtonItem(label = "Option 4", isChecked = false), + ) + ), + onItemClick = {} + ) + } +} + +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +private fun HorizontalLabelRadioButtonGroupPreview() { + MaterialTheme { + HorizontalLabelRadioButtonGroup( + viewEntity = RadioGroupViewEntity( + listOf( + RadioButtonItem(label = "Option 1", isChecked = true), + RadioButtonItem(label = "Option 2", isChecked = false), + RadioButtonItem(label = "Option 3", isChecked = false), + RadioButtonItem(label = "Option 4", isChecked = false), + ) + ), + onItemClick = {} + ) + } +} \ No newline at end of file diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/RssiIcon.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RssiIcon.kt index eae2d601..ae32aa1d 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/RssiIcon.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RssiIcon.kt @@ -33,7 +33,9 @@ package no.nordicsemi.android.common.ui.view import androidx.annotation.DrawableRes import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.NetworkWifi import androidx.compose.material.icons.filled.NetworkWifi1Bar @@ -45,6 +47,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.ui.R private const val MEDIUM_RSSI = -80 @@ -62,7 +66,6 @@ fun RssiIcon(rssi: Int) { style = MaterialTheme.typography.labelSmall ) } - } @DrawableRes @@ -81,3 +84,20 @@ fun getWiFiRes(rssi: Int): ImageVector { else -> Icons.Default.NetworkWifi } } + +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true, widthDp = 500) +@Composable +private fun RssiIconPreview() { + MaterialTheme { + Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { + RssiIcon(rssi = -30) + RssiIcon(rssi = -40) + RssiIcon(rssi = -50) + RssiIcon(rssi = -60) + RssiIcon(rssi = -70) + RssiIcon(rssi = -80) + RssiIcon(rssi = -90) + RssiIcon(rssi = -100) + } + } +} \ No newline at end of file diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/SectionTitle.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/SectionTitle.kt index a89897f2..7adb45bf 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/SectionTitle.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/SectionTitle.kt @@ -35,6 +35,11 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.AddHome +import androidx.compose.material.icons.filled.Home +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -44,6 +49,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -88,3 +94,11 @@ fun SectionTitle( modifier = modifier ) } + +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +private fun SectionTitlePreview() { + MaterialTheme { + SectionTitle(icon = Icons.Default.Home, title = "Title") + } +} \ No newline at end of file diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/VerticalDivider.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/VerticalDivider.kt index 71735dd2..e981d99d 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/VerticalDivider.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/VerticalDivider.kt @@ -36,11 +36,15 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -57,4 +61,25 @@ fun VerticalDivider( .clip(RoundedCornerShape(10.dp)) .background(color) ) { } +} + +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +private fun VerticalDividerInWizardPreview() { + MaterialTheme { + WizardStepComponent( + icon = Icons.Default.Warning, + title = "Identify", + state = WizardStepState.COMPLETED, + decor = WizardStepAction.Action( + text = "Action", + onClick = { } + ), + ) { + Text(text = "Action 1") + Text(text = "Action 2") + Text(text = "Action 3") + Text(text = "Action 4") + } + } } \ No newline at end of file diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/WarningView.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WarningView.kt index 2f2dd71e..a0394b12 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/WarningView.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WarningView.kt @@ -37,19 +37,21 @@ import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.ui.view.internal.BigIcon import no.nordicsemi.android.common.ui.view.internal.Hint -import no.nordicsemi.android.common.ui.view.internal.Title @Composable fun WarningView( @@ -60,19 +62,14 @@ fun WarningView( hintTextAlign: TextAlign? = TextAlign.Center, content: @Composable ColumnScope.() -> Unit = {} ) { - Column( - verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically), - horizontalAlignment = Alignment.CenterHorizontally, + WarningView( + painterResource = rememberVectorPainter(image = imageVector), + title = title, + hint = AnnotatedString(text = hint), modifier = modifier, - ) { - BigIcon(imageVector = imageVector) - - Title(text = title) - - Hint(text = hint, textAlign = hintTextAlign) - - content() - } + hintTextAlign = hintTextAlign, + content = content, + ) } @Composable @@ -84,19 +81,32 @@ fun WarningView( hintTextAlign: TextAlign? = TextAlign.Center, content: @Composable ColumnScope.() -> Unit = {} ) { - Column( + WarningView( + painterResource = rememberVectorPainter(image = imageVector), + title = title, + hint = hint, modifier = modifier, - verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - BigIcon(imageVector = imageVector) - - Title(text = title) - - Hint(text = hint, textAlign = hintTextAlign) + hintTextAlign = hintTextAlign, + content = content, + ) +} - content() - } +@Composable +fun WarningView( + painterResource: Painter, + title: String, + hint: AnnotatedString, + modifier: Modifier = Modifier, + hintTextAlign: TextAlign? = TextAlign.Center, + content: @Composable ColumnScope.() -> Unit = {} +) { + WarningView( + painterResource = painterResource, + title = { Text(text = title) }, + hint = { Hint(text = hint, textAlign = hintTextAlign) }, + modifier = modifier, + content = content, + ) } @Composable @@ -107,6 +117,40 @@ fun WarningView( modifier: Modifier = Modifier, hintTextAlign: TextAlign? = TextAlign.Center, content: @Composable ColumnScope.() -> Unit = {} +) { + WarningView( + painterResource = painterResource, + title = { Text(text = title) }, + hint = { Hint(text = hint, textAlign = hintTextAlign) }, + modifier = modifier, + content = content, + ) +} + +@Composable +fun WarningView( + imageVector: ImageVector, + title: @Composable () -> Unit, + hint: @Composable () -> Unit, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit = {} +) { + WarningView( + painterResource = rememberVectorPainter(image = imageVector), + title = title, + hint = hint, + modifier = modifier, + content = content, + ) +} + +@Composable +fun WarningView( + painterResource: Painter, + title: @Composable () -> Unit, + hint: @Composable () -> Unit, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit = {} ) { Column( modifier = modifier, @@ -115,15 +159,15 @@ fun WarningView( ) { BigIcon(painterResource = painterResource) - Title(text = title) + title() - Hint(text = hint, textAlign = hintTextAlign) + hint() content() } } -@Preview +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) @Composable private fun WarningViewPreview() { MaterialTheme { @@ -132,6 +176,10 @@ private fun WarningViewPreview() { title = "Warning", hint = "This is a warning view", modifier = Modifier.fillMaxSize(), - ) + ) { + Button(onClick = { /*TODO*/ }) { + Text(text = "This is content") + } + } } } \ No newline at end of file diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt index b8d20ae7..dec3ec6b 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt @@ -31,29 +31,50 @@ package no.nordicsemi.android.common.ui.view +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AccountBox +import androidx.compose.material.icons.filled.AccountCircle +import androidx.compose.material.icons.filled.Build +import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp sealed class WizardStepAction { @@ -174,7 +195,6 @@ private fun ActionButton( OutlinedButton( modifier = modifier, onClick = action.onClick, - enabled = action.enabled, colors = if (action.dangerous) { ButtonDefaults.outlinedButtonColors( contentColor = MaterialTheme.colorScheme.error @@ -202,4 +222,116 @@ private fun ActionButton( Text(text = action.text) } } +} + +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +private fun WizardScreen() { + Box( + modifier = Modifier, + contentAlignment = Alignment.TopCenter + ) { + OutlinedCard( + modifier = Modifier + .verticalScroll(rememberScrollState()) + .widthIn(max = 600.dp) + .padding(all = 16.dp), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) { + WizardStepComponent( + icon = Icons.Default.Warning, + title = "Identify", + state = WizardStepState.COMPLETED, + decor = WizardStepAction.Action( + text = "Action", + onClick = { } + ), + ) { + Text(text = "Identified") + } + WizardStepComponent( + icon = Icons.Default.AccountBox, + title = "Account", + state = WizardStepState.CURRENT, + decor = WizardStepAction.Action( + text = "Action", + onClick = { } + ), + ) { + Text(text = "Select color") + } + WizardStepComponent( + icon = Icons.Default.AccountCircle, + title = "Connect", + state = WizardStepState.CURRENT, + decor = WizardStepAction.ProgressIndicator, + showVerticalDivider = false, + ) { + ProgressItem( + text = "Completed", + status = ProgressItemStatus.SUCCESS, + iconRightPadding = 24.dp, + ) + + val infiniteTransition = + rememberInfiniteTransition(label = "ProgressTransition") + val progress by infiniteTransition.animateFloat( + initialValue = 0.0f, + targetValue = 1.0f, + animationSpec = infiniteRepeatable( + animation = tween(10000, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ), + label = "Progress" + ) + + ProgressItem( + text = "In progress", + status = ProgressItemStatus.WORKING, + iconRightPadding = 24.dp, + ) { + LinearProgressIndicator( + progress = { progress }, + modifier = Modifier.fillMaxWidth(), + trackColor = MaterialTheme.colorScheme.surfaceVariant, + drawStopIndicator = {} + ) + Text( + text = "%.1f%%".format(progress * 100), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.End + ) + } + + ProgressItem( + text = "Future", + status = ProgressItemStatus.DISABLED, + iconRightPadding = 24.dp, + ) + + ProgressItem( + text = "Error happened", + status = ProgressItemStatus.ERROR, + iconRightPadding = 24.dp, + ) + } + WizardStepComponent( + icon = Icons.Default.Build, + title = "Destroy", + state = WizardStepState.INACTIVE, + decor = WizardStepAction.Action( + text = "Terminate", + dangerous = true, + onClick = { } + ), + ) { + Text(text = "Engage warp 4") + } + } + } + } } \ No newline at end of file diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 1299ed8f..d9e719d5 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -35,5 +35,5 @@ Back Menu - %d dBm + %d dBm From 09272263c346df7e8e8ab709cbe284103fdead4b Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Tue, 18 Jun 2024 23:59:07 +0200 Subject: [PATCH 18/27] Breaking: Wizard improvements --- .../common/test/main/page/WizardPage.kt | 14 +- .../android/common/ui/view/ProgressItem.kt | 27 +- .../common/ui/view/WizardStepComponent.kt | 289 ++++++++++-------- 3 files changed, 204 insertions(+), 126 deletions(-) diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt index 58e4c3fa..1438e87f 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt @@ -63,6 +63,7 @@ import no.nordicsemi.android.common.theme.NordicTheme import no.nordicsemi.android.common.ui.view.PagerViewItem import no.nordicsemi.android.common.ui.view.ProgressItem import no.nordicsemi.android.common.ui.view.ProgressItemStatus +import no.nordicsemi.android.common.ui.view.StatusItem import no.nordicsemi.android.common.ui.view.WizardStepAction import no.nordicsemi.android.common.ui.view.WizardStepComponent import no.nordicsemi.android.common.ui.view.WizardStepState @@ -92,7 +93,9 @@ private fun WizardScreen() { onClick = { } ), ) { - Text(text = stringResource(id = R.string.wizard_text_completed)) + StatusItem { + Text(text = stringResource(id = R.string.wizard_text_completed)) + } } WizardStepComponent( icon = Icons.Default.AccountBox, @@ -103,14 +106,15 @@ private fun WizardScreen() { onClick = { } ), ) { - Text(text = stringResource(id = R.string.wizard_text_current)) + StatusItem { + Text(text = stringResource(id = R.string.wizard_text_current)) + } } WizardStepComponent( icon = Icons.Default.AccountCircle, title = stringResource(id = R.string.wizard_step_in_progress), state = WizardStepState.CURRENT, decor = WizardStepAction.ProgressIndicator, - showVerticalDivider = false, ) { ProgressItem( text = stringResource(id = R.string.wizard_text_completed), @@ -169,7 +173,9 @@ private fun WizardScreen() { onClick = { } ), ) { - Text(text = stringResource(id = R.string.wizard_text_inactive)) + StatusItem { + Text(text = stringResource(id = R.string.wizard_text_inactive)) + } } } } diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt index 752af179..0d5caaaa 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt @@ -32,11 +32,15 @@ package no.nordicsemi.android.common.ui.view import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator @@ -65,7 +69,7 @@ fun ProgressItem( text: String, status: ProgressItemStatus, modifier: Modifier = Modifier, - iconRightPadding: Dp = 16.dp, + iconRightPadding: Dp = 24.dp, content: @Composable ColumnScope.() -> Unit = {} ) { Row( @@ -92,6 +96,27 @@ fun ProgressItem( } } +@Composable +fun StatusItem( + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit, +) { + Row( + modifier = modifier + .height(IntrinsicSize.Min) + ) { + VerticalDivider( + modifier = Modifier.padding(start = 10.dp, end = 26.dp) + ) + Column( + modifier = Modifier.padding(start = 8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + content() + } + } +} + @Composable private fun ProgressItemStatus.toIconColor(): Color { return when (this) { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt index dec3ec6b..8ddc9b8a 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt @@ -31,6 +31,7 @@ package no.nordicsemi.android.common.ui.view +import android.content.res.Configuration import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.RepeatMode import androidx.compose.animation.core.animateFloat @@ -41,11 +42,10 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn @@ -64,16 +64,21 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -124,7 +129,42 @@ fun WizardStepComponent( decor: WizardStepAction? = null, color: Color = MaterialTheme.colorScheme.surface, contentColor: Color = contentColorFor(color), - showVerticalDivider: Boolean = true, + content: @Composable ColumnScope.() -> Unit +) { + WizardStepComponent( + icon = icon, + title = { + Text( + text = title, + style = MaterialTheme.typography.titleLarge, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + }, + state = state, + modifier = modifier, + decor = decor, + contentColor = contentColor, + content = content, + ) +} + +/** + * A wizard step component. + * + * @param icon The icon will be placed in a circular container. + * @param title The title of the step. + * @param state Current state of the step. + * @param decor An optional action or decoration that will be shown on the right. + */ +@Composable +fun WizardStepComponent( + icon: ImageVector, + title: @Composable () -> Unit, + state: WizardStepState, + modifier: Modifier = Modifier, + decor: WizardStepAction? = null, + contentColor: Color = LocalContentColor.current, content: @Composable ColumnScope.() -> Unit ) { Column(modifier = modifier) { @@ -135,11 +175,9 @@ fun WizardStepComponent( Spacer(modifier = Modifier.size(16.dp)) - Text( - text = title, - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.weight(1f) - ) + Box(modifier = Modifier.weight(1.0f)) { + title() + } Spacer(modifier = Modifier.size(16.dp)) @@ -159,27 +197,17 @@ fun WizardStepComponent( } } - Row( - modifier = Modifier - .padding(vertical = 16.dp) - .height(IntrinsicSize.Min) + CompositionLocalProvider( + LocalContentColor provides if (state == WizardStepState.INACTIVE) + contentColor.copy(alpha = 0.38f) else contentColor ) { - if (showVerticalDivider) { - VerticalDivider( - modifier = Modifier.padding(start = 18.dp, end = 26.dp) - ) - } - - CompositionLocalProvider( - LocalContentColor provides if (state == WizardStepState.INACTIVE) - contentColor.copy(alpha = 0.38f) else contentColor + Column( + modifier = Modifier + .padding(vertical = 16.dp) + .padding(start = 8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), ) { - Column( - modifier = Modifier.padding(start = 8.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - content() - } + content() } } } @@ -224,112 +252,131 @@ private fun ActionButton( } } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable private fun WizardScreen() { - Box( - modifier = Modifier, - contentAlignment = Alignment.TopCenter - ) { - OutlinedCard( - modifier = Modifier - .verticalScroll(rememberScrollState()) - .widthIn(max = 600.dp) - .padding(all = 16.dp), + MaterialTheme { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.TopCenter ) { - Column( + OutlinedCard( modifier = Modifier - .fillMaxWidth() - .padding(16.dp), + .verticalScroll(rememberScrollState()) + .widthIn(max = 600.dp) + .padding(all = 16.dp), ) { - WizardStepComponent( - icon = Icons.Default.Warning, - title = "Identify", - state = WizardStepState.COMPLETED, - decor = WizardStepAction.Action( - text = "Action", - onClick = { } - ), - ) { - Text(text = "Identified") - } - WizardStepComponent( - icon = Icons.Default.AccountBox, - title = "Account", - state = WizardStepState.CURRENT, - decor = WizardStepAction.Action( - text = "Action", - onClick = { } - ), - ) { - Text(text = "Select color") - } - WizardStepComponent( - icon = Icons.Default.AccountCircle, - title = "Connect", - state = WizardStepState.CURRENT, - decor = WizardStepAction.ProgressIndicator, - showVerticalDivider = false, + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), ) { - ProgressItem( - text = "Completed", - status = ProgressItemStatus.SUCCESS, - iconRightPadding = 24.dp, - ) - - val infiniteTransition = - rememberInfiniteTransition(label = "ProgressTransition") - val progress by infiniteTransition.animateFloat( - initialValue = 0.0f, - targetValue = 1.0f, - animationSpec = infiniteRepeatable( - animation = tween(10000, easing = LinearEasing), - repeatMode = RepeatMode.Restart + WizardStepComponent( + icon = Icons.Default.Warning, + title = "Identify", + state = WizardStepState.COMPLETED, + decor = WizardStepAction.Action( + text = "Action", + onClick = { } + ), + ) { + StatusItem { + Text(text = "Identified") + } + } + WizardStepComponent( + icon = Icons.Default.AccountBox, + title = "Very Long Title That Won't Fit", + state = WizardStepState.CURRENT, + decor = WizardStepAction.Action( + text = "Action", + onClick = { } ), - label = "Progress" - ) - - ProgressItem( - text = "In progress", - status = ProgressItemStatus.WORKING, - iconRightPadding = 24.dp, ) { - LinearProgressIndicator( - progress = { progress }, - modifier = Modifier.fillMaxWidth(), - trackColor = MaterialTheme.colorScheme.surfaceVariant, - drawStopIndicator = {} + StatusItem { + Text(text = "Select color") + } + } + WizardStepComponent( + icon = Icons.Default.AccountCircle, + title = "Connect", + state = WizardStepState.CURRENT, + decor = WizardStepAction.ProgressIndicator, + ) { + ProgressItem( + text = "Completed", + status = ProgressItemStatus.SUCCESS, ) - Text( - text = "%.1f%%".format(progress * 100), - modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.End + + val infiniteTransition = + rememberInfiniteTransition(label = "ProgressTransition") + val progress by infiniteTransition.animateFloat( + initialValue = 0.0f, + targetValue = 1.0f, + animationSpec = infiniteRepeatable( + animation = tween(10000, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ), + label = "Progress" ) - } - ProgressItem( - text = "Future", - status = ProgressItemStatus.DISABLED, - iconRightPadding = 24.dp, - ) + ProgressItem( + text = "In progress", + status = ProgressItemStatus.WORKING, + ) { + LinearProgressIndicator( + progress = { progress }, + modifier = Modifier.fillMaxWidth(), + trackColor = MaterialTheme.colorScheme.surfaceVariant, + drawStopIndicator = {} + ) + Text( + text = "%.1f%%".format(progress * 100), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.End + ) + } - ProgressItem( - text = "Error happened", - status = ProgressItemStatus.ERROR, - iconRightPadding = 24.dp, - ) - } - WizardStepComponent( - icon = Icons.Default.Build, - title = "Destroy", - state = WizardStepState.INACTIVE, - decor = WizardStepAction.Action( - text = "Terminate", - dangerous = true, - onClick = { } - ), - ) { - Text(text = "Engage warp 4") + ProgressItem( + text = "Future", + status = ProgressItemStatus.DISABLED, + ) + + ProgressItem( + text = "Error happened", + status = ProgressItemStatus.ERROR, + ) + + StatusItem { + Text(text = "Connect to the device") + } + } + WizardStepComponent( + icon = Icons.Default.Build, + title = "Destroy", + state = WizardStepState.INACTIVE, + decor = WizardStepAction.Action( + text = "Terminate", + dangerous = true, + onClick = { } + ), + ) { + StatusItem { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = "Engage warp 4", + modifier = Modifier.weight(1f) + ) + var checked by rememberSaveable { mutableStateOf(false) } + Switch( + checked = checked, + onCheckedChange = { checked = it }, + ) + } + } + } } } } From 9d199f84bf98b167b1b83e568231e6f4b0756616 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 19 Jun 2024 13:27:34 +0200 Subject: [PATCH 19/27] Wizard improvements --- .../common/test/main/page/WizardPage.kt | 6 +- .../android/common/ui/view/ProgressItem.kt | 58 ++++++++++--------- .../common/ui/view/WizardStepComponent.kt | 42 ++++++++------ 3 files changed, 56 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt index 1438e87f..ba5250b3 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt @@ -119,7 +119,6 @@ private fun WizardScreen() { ProgressItem( text = stringResource(id = R.string.wizard_text_completed), status = ProgressItemStatus.SUCCESS, - iconRightPadding = 24.dp, ) val infiniteTransition = rememberInfiniteTransition(label = "ProgressTransition") @@ -134,10 +133,9 @@ private fun WizardScreen() { ) ProgressItem( - text = stringResource(id = R.string.wizard_task_in_progress), status = ProgressItemStatus.WORKING, - iconRightPadding = 24.dp, ) { + Text(text = stringResource(id = R.string.wizard_task_in_progress)) LinearProgressIndicator( progress = { progress }, modifier = Modifier.fillMaxWidth(), @@ -154,13 +152,11 @@ private fun WizardScreen() { ProgressItem( text = stringResource(id = R.string.wizard_task_next), status = ProgressItemStatus.DISABLED, - iconRightPadding = 24.dp, ) ProgressItem( text = stringResource(id = R.string.wizard_task_error), status = ProgressItemStatus.ERROR, - iconRightPadding = 24.dp, ) } WizardStepComponent( diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt index 0d5caaaa..fbcfed93 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt @@ -46,7 +46,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -69,12 +68,30 @@ fun ProgressItem( text: String, status: ProgressItemStatus, modifier: Modifier = Modifier, + verticalAlignment: Alignment.Vertical = Alignment.Top, + iconRightPadding: Dp = 24.dp, +) { + ProgressItem( + status = status, + modifier = modifier, + verticalAlignment = verticalAlignment, + iconRightPadding = iconRightPadding, + ) { + Text(text = text) + } +} + +@Composable +fun ProgressItem( + status: ProgressItemStatus, + modifier: Modifier = Modifier, + verticalAlignment: Alignment.Vertical = Alignment.Top, iconRightPadding: Dp = 24.dp, content: @Composable ColumnScope.() -> Unit = {} ) { Row( modifier = modifier, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = verticalAlignment, ) { Icon( painter = painterResource(status.toImageRes()), @@ -84,14 +101,10 @@ fun ProgressItem( Spacer(modifier = Modifier.width(iconRightPadding)) - Column { - ProvideTextStyle(value = MaterialTheme.typography.bodyMedium) { - Text( - text = text, - color = status.toTextColor(), - ) - content() - } + Column( + modifier = Modifier.padding(vertical = 2.dp) + ) { + content() } } } @@ -102,14 +115,15 @@ fun StatusItem( content: @Composable ColumnScope.() -> Unit, ) { Row( - modifier = modifier + modifier = Modifier .height(IntrinsicSize.Min) + .then(modifier), ) { VerticalDivider( - modifier = Modifier.padding(start = 10.dp, end = 26.dp) + modifier = Modifier.padding(start = 10.dp, end = 34.dp) ) Column( - modifier = Modifier.padding(start = 8.dp), + modifier = Modifier.padding(vertical = 2.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { content() @@ -127,16 +141,6 @@ private fun ProgressItemStatus.toIconColor(): Color { } } -@Composable -private fun ProgressItemStatus.toTextColor(): Color { - return when (this) { - ProgressItemStatus.DISABLED -> LocalContentColor.current.copy(alpha = 0.38f) - ProgressItemStatus.WORKING, - ProgressItemStatus.SUCCESS, - ProgressItemStatus.ERROR -> LocalContentColor.current - } -} - @DrawableRes private fun ProgressItemStatus.toImageRes(): Int { return when (this) { @@ -147,14 +151,14 @@ private fun ProgressItemStatus.toImageRes(): Int { } } -@Preview +@Preview(showBackground = true) @Composable private fun ProgressItemPreview_Working() { MaterialTheme { ProgressItem( - text = "Uploading", - status = ProgressItemStatus.WORKING, + status = ProgressItemStatus.ERROR, ) { + Text(text = "Uploading...") LinearProgressIndicator( progress = { 0.3f }, modifier = Modifier.fillMaxWidth(), @@ -173,7 +177,7 @@ private fun ProgressItemPreview_Working() { private fun ProgressItemPreview_Success() { MaterialTheme { ProgressItem( - text = "Completed", + text = "Success!\nBut there's more!\nAnd even more!", status = ProgressItemStatus.SUCCESS, ) } diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt index 8ddc9b8a..c9d22422 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WizardStepComponent.kt @@ -31,7 +31,6 @@ package no.nordicsemi.android.common.ui.view -import android.content.res.Configuration import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.RepeatMode import androidx.compose.animation.core.animateFloat @@ -64,9 +63,9 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Switch import androidx.compose.material3.Text -import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue @@ -77,6 +76,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -127,8 +127,8 @@ fun WizardStepComponent( state: WizardStepState, modifier: Modifier = Modifier, decor: WizardStepAction? = null, - color: Color = MaterialTheme.colorScheme.surface, - contentColor: Color = contentColorFor(color), + contentColor: Color = LocalContentColor.current, + contentStyle: TextStyle = MaterialTheme.typography.bodyMedium, content: @Composable ColumnScope.() -> Unit ) { WizardStepComponent( @@ -145,6 +145,7 @@ fun WizardStepComponent( modifier = modifier, decor = decor, contentColor = contentColor, + contentStyle = contentStyle, content = content, ) } @@ -165,6 +166,7 @@ fun WizardStepComponent( modifier: Modifier = Modifier, decor: WizardStepAction? = null, contentColor: Color = LocalContentColor.current, + contentStyle: TextStyle = MaterialTheme.typography.bodyMedium, content: @Composable ColumnScope.() -> Unit ) { Column(modifier = modifier) { @@ -207,7 +209,9 @@ fun WizardStepComponent( .padding(start = 8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { - content() + ProvideTextStyle(value = contentStyle) { + content() + } } } } @@ -252,7 +256,7 @@ private fun ActionButton( } } -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview(showBackground = true) @Composable private fun WizardScreen() { MaterialTheme { @@ -321,20 +325,22 @@ private fun WizardScreen() { ) ProgressItem( - text = "In progress", status = ProgressItemStatus.WORKING, ) { - LinearProgressIndicator( - progress = { progress }, - modifier = Modifier.fillMaxWidth(), - trackColor = MaterialTheme.colorScheme.surfaceVariant, - drawStopIndicator = {} - ) - Text( - text = "%.1f%%".format(progress * 100), - modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.End - ) + Column { + Text(text = "In progress") + LinearProgressIndicator( + progress = { progress }, + modifier = Modifier.fillMaxWidth(), + trackColor = MaterialTheme.colorScheme.surfaceVariant, + drawStopIndicator = {} + ) + Text( + text = "%.1f%%".format(progress * 100), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.End + ) + } } ProgressItem( From aa76d84bbd3a25af7a99362af9a110641e293d8d Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 19 Jun 2024 13:28:07 +0200 Subject: [PATCH 20/27] WPreviews simplified --- .../common/permissions/ble/view/BluetoothDisabledView.kt | 2 +- .../permissions/ble/view/BluetoothNotAvailableView.kt | 2 +- .../permissions/ble/view/LocationPermissionRequiredView.kt | 2 +- .../permissions/internet/view/InternetNotAvailableView.kt | 2 +- .../android/common/permissions/nfc/view/NfcDisabledView.kt | 2 +- .../common/permissions/nfc/view/NfcNotAvailableView.kt | 2 +- .../common/permissions/wifi/view/LocationDisabledView.kt | 2 +- .../permissions/wifi/view/LocationPermissionRequiredView.kt | 2 +- .../common/permissions/wifi/view/WifiDisabledView.kt | 2 +- .../common/permissions/wifi/view/WifiNotAvailableView.kt | 2 +- .../permissions/wifi/view/WifiPermissionRequiredView.kt | 2 +- .../no/nordicsemi/android/common/ui/view/CircularIcon.kt | 2 +- .../nordicsemi/android/common/ui/view/FloatingActionMenu.kt | 2 +- .../no/nordicsemi/android/common/ui/view/NordicAppBar.kt | 2 +- .../nordicsemi/android/common/ui/view/NordicLargeAppBar.kt | 2 +- .../nordicsemi/android/common/ui/view/NordicMediumAppBar.kt | 2 +- .../no/nordicsemi/android/common/ui/view/ProgressItem.kt | 6 +++--- .../nordicsemi/android/common/ui/view/RadioButtonGroup.kt | 4 ++-- .../java/no/nordicsemi/android/common/ui/view/RssiIcon.kt | 2 +- .../no/nordicsemi/android/common/ui/view/SectionTitle.kt | 2 +- .../no/nordicsemi/android/common/ui/view/VerticalDivider.kt | 2 +- .../no/nordicsemi/android/common/ui/view/WarningView.kt | 2 +- 22 files changed, 25 insertions(+), 25 deletions(-) diff --git a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt index 5808254f..0db1c294 100644 --- a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt +++ b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothDisabledView.kt @@ -73,7 +73,7 @@ private fun enableBluetooth(context: Context) { context.startActivity(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun BluetoothDisabledView_Preview() { MaterialTheme { diff --git a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt index 070da2f7..80b5d774 100644 --- a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt +++ b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/BluetoothNotAvailableView.kt @@ -56,7 +56,7 @@ internal fun BluetoothNotAvailableView() { ) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun BluetoothNotAvailableView_Preview() { MaterialTheme { diff --git a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt index bd71e9cd..70d22325 100644 --- a/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt +++ b/permissions-ble/src/main/java/no/nordicsemi/android/common/permissions/ble/view/LocationPermissionRequiredView.kt @@ -125,7 +125,7 @@ private fun openPermissionSettings(context: Context) { ) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun LocationPermissionRequiredView_Preview() { MaterialTheme { diff --git a/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt b/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt index 4d8593dd..60399697 100644 --- a/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt +++ b/permissions-internet/src/main/java/no/nordicsemi/android/common/permissions/internet/view/InternetNotAvailableView.kt @@ -56,7 +56,7 @@ internal fun InternetNotAvailableView() { ) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun BluetoothNotAvailableView_Preview() { MaterialTheme { diff --git a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt index ddab73a0..493ab8aa 100644 --- a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt +++ b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcDisabledView.kt @@ -70,7 +70,7 @@ private fun enableNfc(context: Context) { context.startActivity(Intent(Settings.ACTION_NFC_SETTINGS)) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun NfcDisabledView_Preview() { MaterialTheme { diff --git a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt index 78e13c7c..86e5f046 100644 --- a/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt +++ b/permissions-nfc/src/main/java/no/nordicsemi/android/common/permissions/nfc/view/NfcNotAvailableView.kt @@ -55,7 +55,7 @@ internal fun NfcNotAvailableView() { ) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun NfcNotAvailableView_Preview() { MaterialTheme { diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt index 759cfe4d..ca47a4b8 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationDisabledView.kt @@ -68,7 +68,7 @@ private fun enableLocation(context: Context) { context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun LocationDisabledViewPreview() { MaterialTheme { diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt index 3ef54c1e..9b627054 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/LocationPermissionRequiredView.kt @@ -121,7 +121,7 @@ private fun openPermissionSettings(context: Context) { ) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun LocationPermissionRequiredView_Preview() { MaterialTheme { diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt index aa6e270a..9907da57 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiDisabledView.kt @@ -68,7 +68,7 @@ private fun enableWifi(context: Context) { context.startActivity(Intent(Settings.ACTION_WIFI_SETTINGS)) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun WifiDisabledViewPreview() { MaterialTheme { diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt index 31af9a93..88a5f6b2 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiNotAvailableView.kt @@ -53,7 +53,7 @@ internal fun WifiNotAvailableView() { ) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun WifiNotAvailableView_Preview() { MaterialTheme { diff --git a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt index b297b65d..0b708e31 100644 --- a/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt +++ b/permissions-wifi/src/main/java/no/nordicsemi/android/common/permissions/wifi/view/WifiPermissionRequiredView.kt @@ -116,7 +116,7 @@ private fun openPermissionSettings(context: Context) { ) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun WifiPermissionRequiredViewPreview() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/CircularIcon.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/CircularIcon.kt index 33e07276..e5fe78c3 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/CircularIcon.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/CircularIcon.kt @@ -92,7 +92,7 @@ fun CircularIcon( ) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun CircularIconPreview() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/FloatingActionMenu.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/FloatingActionMenu.kt index ec43b4df..2d86c0df 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/FloatingActionMenu.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/FloatingActionMenu.kt @@ -253,7 +253,7 @@ private fun Scrim(onClose: () -> Unit, modifier: Modifier = Modifier) { ) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true, heightDp = 200) +@Preview(showBackground = true, heightDp = 200) @Composable private fun FloatingActionMenuPreview() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicAppBar.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicAppBar.kt index a076de36..4a22b7d1 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicAppBar.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicAppBar.kt @@ -108,7 +108,7 @@ fun NordicAppBar( } @OptIn(ExperimentalMaterial3Api::class) -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun NordicAppBarPreview() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt index 54eba1a5..38b2c474 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicLargeAppBar.kt @@ -110,7 +110,7 @@ fun NordicLargeAppBar( } @OptIn(ExperimentalMaterial3Api::class) -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun NordicLargeAppBarPreview() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicMediumAppBar.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicMediumAppBar.kt index 2ea5ed2c..07e3b61e 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicMediumAppBar.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/NordicMediumAppBar.kt @@ -107,7 +107,7 @@ fun NordicMediumAppBar( } @OptIn(ExperimentalMaterial3Api::class) -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun NordicMediumAppBarPreview() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt index fbcfed93..19f80357 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt @@ -172,7 +172,7 @@ private fun ProgressItemPreview_Working() { } } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun ProgressItemPreview_Success() { MaterialTheme { @@ -183,7 +183,7 @@ private fun ProgressItemPreview_Success() { } } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun ProgressItemPreview_Disabled() { MaterialTheme { @@ -194,7 +194,7 @@ private fun ProgressItemPreview_Disabled() { } } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun ProgressItemPreview_Error() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/RadioButtonGroup.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RadioButtonGroup.kt index 915bb6cc..76d5548a 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/RadioButtonGroup.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RadioButtonGroup.kt @@ -88,7 +88,7 @@ fun HorizontalLabelRadioButtonGroup( } } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun RadioButtonGroupPreview() { MaterialTheme { @@ -106,7 +106,7 @@ private fun RadioButtonGroupPreview() { } } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun HorizontalLabelRadioButtonGroupPreview() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/RssiIcon.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RssiIcon.kt index ae32aa1d..5211f32e 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/RssiIcon.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/RssiIcon.kt @@ -85,7 +85,7 @@ fun getWiFiRes(rssi: Int): ImageVector { } } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true, widthDp = 500) +@Preview(showBackground = true, widthDp = 500) @Composable private fun RssiIconPreview() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/SectionTitle.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/SectionTitle.kt index 7adb45bf..84b09c13 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/SectionTitle.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/SectionTitle.kt @@ -95,7 +95,7 @@ fun SectionTitle( ) } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun SectionTitlePreview() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/VerticalDivider.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/VerticalDivider.kt index e981d99d..f7206176 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/VerticalDivider.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/VerticalDivider.kt @@ -63,7 +63,7 @@ fun VerticalDivider( ) { } } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun VerticalDividerInWizardPreview() { MaterialTheme { diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/WarningView.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WarningView.kt index a0394b12..f6734432 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/WarningView.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/WarningView.kt @@ -167,7 +167,7 @@ fun WarningView( } } -@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Preview(showBackground = true) @Composable private fun WarningViewPreview() { MaterialTheme { From 99a62dc65fdc9be0599283b5c3f05fd426315316 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 19 Jun 2024 13:28:23 +0200 Subject: [PATCH 21/27] Scanner migrated to :ui --- .../android/kotlin/ble/ui/scanner/main/DeviceListItem.kt | 4 ++-- .../kotlin/ble/ui/scanner/view/DeviceConnectingView.kt | 2 +- .../kotlin/ble/ui/scanner/view/DeviceDisconnectedView.kt | 2 +- .../android/kotlin/ble/ui/scanner/view/ScannerAppBar.kt | 2 +- .../kotlin/ble/ui/scanner/view/internal/ScanEmptyView.kt | 2 +- .../kotlin/ble/ui/scanner/view/internal/ScanErrorView.kt | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItem.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItem.kt index 5be06d0e..297aeb31 100644 --- a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItem.kt +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/main/DeviceListItem.kt @@ -48,8 +48,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.CircularIcon -import no.nordicsemi.android.common.theme.view.RssiIcon +import no.nordicsemi.android.common.ui.view.CircularIcon +import no.nordicsemi.android.common.ui.view.RssiIcon import no.nordicsemi.android.kotlin.ble.ui.scanner.R @Composable diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceConnectingView.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceConnectingView.kt index a5a7476d..a4d78fff 100644 --- a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceConnectingView.kt +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceConnectingView.kt @@ -52,7 +52,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.CircularIcon +import no.nordicsemi.android.common.ui.view.CircularIcon import no.nordicsemi.android.kotlin.ble.ui.scanner.R @Composable diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceDisconnectedView.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceDisconnectedView.kt index f0695aca..e1bbc396 100644 --- a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceDisconnectedView.kt +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/DeviceDisconnectedView.kt @@ -52,7 +52,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.CircularIcon +import no.nordicsemi.android.common.ui.view.CircularIcon import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.ui.scanner.R diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/ScannerAppBar.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/ScannerAppBar.kt index 51d98cbe..73779dd5 100644 --- a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/ScannerAppBar.kt +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/ScannerAppBar.kt @@ -42,7 +42,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp -import no.nordicsemi.android.common.theme.view.NordicAppBar +import no.nordicsemi.android.common.ui.view.NordicAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanEmptyView.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanEmptyView.kt index bc39a5f4..8c001ea4 100644 --- a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanEmptyView.kt +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanEmptyView.kt @@ -50,7 +50,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.core.parseBold import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView import no.nordicsemi.android.kotlin.ble.ui.scanner.R @Composable diff --git a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanErrorView.kt b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanErrorView.kt index a56c12b2..49fe6124 100644 --- a/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanErrorView.kt +++ b/scanner/src/main/java/no/nordicsemi/android/kotlin/ble/ui/scanner/view/internal/ScanErrorView.kt @@ -41,7 +41,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView +import no.nordicsemi.android.common.ui.view.WarningView import no.nordicsemi.android.kotlin.ble.ui.scanner.R @Composable From 754382b694706e49a4ba709c8eb8bd5e05e5bc1a Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 19 Jun 2024 15:38:03 +0200 Subject: [PATCH 22/27] Adjusting PagerView for the display cutout --- .../android/common/ui/view/PagerView.kt | 61 ++++++++++++------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/PagerView.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/PagerView.kt index c335f897..1b14ae26 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/PagerView.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/PagerView.kt @@ -31,9 +31,12 @@ package no.nordicsemi.android.common.ui.view +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState @@ -50,6 +53,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -98,9 +102,13 @@ fun PagerView( ) { val tabIndex = pagerState.currentPage + val edgePadding = maxOf( + contentPadding.calculateLeftPadding(LayoutDirection.Ltr), + contentPadding.calculateRightPadding(LayoutDirection.Rtl) + ) if (scrollable) { ScrollableTabRow( - edgePadding = 0.dp, + edgePadding = edgePadding, selectedTabIndex = tabIndex, containerColor = colorResource(id = R.color.appBarColor), contentColor = MaterialTheme.colorScheme.onPrimary, @@ -126,30 +134,37 @@ fun PagerView( } } } else { - TabRow( - modifier = Modifier.fillMaxWidth(), - selectedTabIndex = tabIndex, - containerColor = colorResource(id = R.color.appBarColor), - contentColor = MaterialTheme.colorScheme.onPrimary, - indicator = @Composable { tabPositions -> - SecondaryIndicator( - Modifier.tabIndicatorOffset(tabPositions[tabIndex]), - color = MaterialTheme.colorScheme.onPrimary - ) - }, + // This box adds a padding on the sides to accommodate the cutout (edge padding). + Box( + modifier = Modifier + .background(color = colorResource(id = R.color.appBarColor)) + .padding(horizontal = edgePadding) ) { - viewEntity.items.forEachIndexed { index, item -> - Tab( - selected = tabIndex == index, - onClick = { - coroutineScope.launch { - pagerState.animateScrollToPage(index) + TabRow( + modifier = Modifier.fillMaxWidth(), + selectedTabIndex = tabIndex, + containerColor = colorResource(id = R.color.appBarColor), + contentColor = MaterialTheme.colorScheme.onPrimary, + indicator = @Composable { tabPositions -> + SecondaryIndicator( + Modifier.tabIndicatorOffset(tabPositions[tabIndex]), + color = MaterialTheme.colorScheme.onPrimary + ) + }, + ) { + viewEntity.items.forEachIndexed { index, item -> + Tab( + selected = tabIndex == index, + onClick = { + coroutineScope.launch { + pagerState.animateScrollToPage(index) + } + }, + text = { + Text(text = item.title) } - }, - text = { - Text(text = item.title) - } - ) + ) + } } } } From efd70cca24d6475af72428c8c67d38528de030a6 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 19 Jun 2024 15:38:30 +0200 Subject: [PATCH 23/27] Applying display cutout to PagerView in sample app --- .../android/common/test/main/Main.kt | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt index 3d310d5c..a6a71932 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/Main.kt @@ -31,8 +31,14 @@ package no.nordicsemi.android.common.test.main -import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.displayCutout +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.union import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.navigation.createSimpleDestination import no.nordicsemi.android.common.navigation.defineDestination @@ -64,9 +70,20 @@ private fun MainScreen() { WizardPage, WarningPage, )) + // Pad content with cutout, at least 16dp on each side. + val padding = WindowInsets.displayCutout + .only(WindowInsetsSides.Horizontal) + .union(WindowInsets(left = 16.dp, right = 16.dp)) + .asPaddingValues() + // Use the greatest padding for the spacing, so that other pages aren't visible + // being the cutout. + val spacing = maxOf( + padding.calculateLeftPadding(LayoutDirection.Ltr), + padding.calculateRightPadding(LayoutDirection.Rtl) + ) PagerView( viewEntity = pages, - contentPadding = PaddingValues(horizontal = 16.dp), - itemSpacing = 16.dp, + contentPadding = padding, + itemSpacing = spacing, ) } From f4dae682552532ae05cc1ad68808733e2e049ad8 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 19 Jun 2024 15:45:38 +0200 Subject: [PATCH 24/27] Centering Wizard in the sample app --- .../common/test/main/page/WizardPage.kt | 182 +++++++++--------- 1 file changed, 96 insertions(+), 86 deletions(-) diff --git a/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt b/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt index ba5250b3..ab8e7f1c 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/main/page/WizardPage.kt @@ -37,9 +37,11 @@ import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.infiniteRepeatable import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons @@ -53,6 +55,7 @@ import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -74,103 +77,110 @@ val WizardPage = PagerViewItem("Wizard") { @Composable private fun WizardScreen() { - OutlinedCard( - modifier = Modifier - .verticalScroll(rememberScrollState()) - .padding(vertical = 16.dp), + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.TopCenter ) { - Column( + OutlinedCard( modifier = Modifier - .fillMaxWidth() - .padding(16.dp), + .verticalScroll(rememberScrollState()) + .padding(vertical = 16.dp), ) { - WizardStepComponent( - icon = Icons.Default.Warning, - title = stringResource(id = R.string.wizard_step_completed), - state = WizardStepState.COMPLETED, - decor = WizardStepAction.Action( - text = stringResource(id = R.string.action_no_op), - onClick = { } - ), + Column( + modifier = Modifier + .widthIn(max = 600.dp) + .fillMaxWidth() + .padding(16.dp), ) { - StatusItem { - Text(text = stringResource(id = R.string.wizard_text_completed)) - } - } - WizardStepComponent( - icon = Icons.Default.AccountBox, - title = stringResource(id = R.string.wizard_step_current), - state = WizardStepState.CURRENT, - decor = WizardStepAction.Action( - text = stringResource(id = R.string.action_no_op), - onClick = { } - ), - ) { - StatusItem { - Text(text = stringResource(id = R.string.wizard_text_current)) + WizardStepComponent( + icon = Icons.Default.Warning, + title = stringResource(id = R.string.wizard_step_completed), + state = WizardStepState.COMPLETED, + decor = WizardStepAction.Action( + text = stringResource(id = R.string.action_no_op), + onClick = { } + ), + ) { + StatusItem { + Text(text = stringResource(id = R.string.wizard_text_completed)) + } } - } - WizardStepComponent( - icon = Icons.Default.AccountCircle, - title = stringResource(id = R.string.wizard_step_in_progress), - state = WizardStepState.CURRENT, - decor = WizardStepAction.ProgressIndicator, - ) { - ProgressItem( - text = stringResource(id = R.string.wizard_text_completed), - status = ProgressItemStatus.SUCCESS, - ) - - val infiniteTransition = rememberInfiniteTransition(label = "ProgressTransition") - val progress by infiniteTransition.animateFloat( - initialValue = 0.0f, - targetValue = 1.0f, - animationSpec = infiniteRepeatable( - animation = tween(10000, easing = LinearEasing), - repeatMode = RepeatMode.Restart + WizardStepComponent( + icon = Icons.Default.AccountBox, + title = stringResource(id = R.string.wizard_step_current), + state = WizardStepState.CURRENT, + decor = WizardStepAction.Action( + text = stringResource(id = R.string.action_no_op), + onClick = { } ), - label = "Progress" - ) - - ProgressItem( - status = ProgressItemStatus.WORKING, ) { - Text(text = stringResource(id = R.string.wizard_task_in_progress)) - LinearProgressIndicator( - progress = { progress }, - modifier = Modifier.fillMaxWidth(), - trackColor = MaterialTheme.colorScheme.surfaceVariant, - drawStopIndicator = {} + StatusItem { + Text(text = stringResource(id = R.string.wizard_text_current)) + } + } + WizardStepComponent( + icon = Icons.Default.AccountCircle, + title = stringResource(id = R.string.wizard_step_in_progress), + state = WizardStepState.CURRENT, + decor = WizardStepAction.ProgressIndicator, + ) { + ProgressItem( + text = stringResource(id = R.string.wizard_text_completed), + status = ProgressItemStatus.SUCCESS, ) - Text( - text = "%.1f%%".format(progress * 100), - modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.End + + val infiniteTransition = + rememberInfiniteTransition(label = "ProgressTransition") + val progress by infiniteTransition.animateFloat( + initialValue = 0.0f, + targetValue = 1.0f, + animationSpec = infiniteRepeatable( + animation = tween(10000, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ), + label = "Progress" ) - } - ProgressItem( - text = stringResource(id = R.string.wizard_task_next), - status = ProgressItemStatus.DISABLED, - ) + ProgressItem( + status = ProgressItemStatus.WORKING, + ) { + Text(text = stringResource(id = R.string.wizard_task_in_progress)) + LinearProgressIndicator( + progress = { progress }, + modifier = Modifier.fillMaxWidth(), + trackColor = MaterialTheme.colorScheme.surfaceVariant, + drawStopIndicator = {} + ) + Text( + text = "%.1f%%".format(progress * 100), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.End + ) + } - ProgressItem( - text = stringResource(id = R.string.wizard_task_error), - status = ProgressItemStatus.ERROR, - ) - } - WizardStepComponent( - icon = Icons.Default.Build, - title = stringResource(id = R.string.wizard_step_inactive), - state = WizardStepState.INACTIVE, - decor = WizardStepAction.Action( - text = stringResource(id = R.string.action_no_op), - dangerous = true, - onClick = { } - ), - ) { - StatusItem { - Text(text = stringResource(id = R.string.wizard_text_inactive)) + ProgressItem( + text = stringResource(id = R.string.wizard_task_next), + status = ProgressItemStatus.DISABLED, + ) + + ProgressItem( + text = stringResource(id = R.string.wizard_task_error), + status = ProgressItemStatus.ERROR, + ) + } + WizardStepComponent( + icon = Icons.Default.Build, + title = stringResource(id = R.string.wizard_step_inactive), + state = WizardStepState.INACTIVE, + decor = WizardStepAction.Action( + text = stringResource(id = R.string.action_no_op), + dangerous = true, + onClick = { } + ), + ) { + StatusItem { + Text(text = stringResource(id = R.string.wizard_text_inactive)) + } } } } From 5b2f32982ea75c5068e413f4ecd2330f7f2ac38a Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 19 Jun 2024 15:46:14 +0200 Subject: [PATCH 25/27] Applying display cutout to the drawer menu --- .../android/common/test/MainActivity.kt | 86 ++++++++++--------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt b/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt index a6b96602..6fad6d0b 100644 --- a/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt +++ b/app/src/main/java/no/nordicsemi/android/common/test/MainActivity.kt @@ -38,6 +38,8 @@ import android.widget.Toast import androidx.activity.SystemBarStyle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.displayCutoutPadding import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -147,52 +149,56 @@ class MainActivity : NordicActivity() { .fillMaxHeight() .verticalScroll(rememberScrollState()), ) { - NordicLogo( - modifier = Modifier - .padding(NavigationDrawerTitleDefaults.ItemPadding) - .padding(vertical = 16.dp) - ) + Column( + modifier = Modifier.displayCutoutPadding(), + ) { + NordicLogo( + modifier = Modifier + .padding(NavigationDrawerTitleDefaults.ItemPadding) + .padding(vertical = 16.dp) + ) - NavigationDrawerTitle( - title = "Menu", - modifier = Modifier.padding(NavigationDrawerTitleDefaults.ItemPadding) - ) + NavigationDrawerTitle( + title = "Menu", + modifier = Modifier.padding(NavigationDrawerTitleDefaults.ItemPadding) + ) - NavigationDrawerItems( - items = menuItems, - navigator = navigator, - onDismiss = { scope.launch { drawerState.close() } }, - modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding) - ) + NavigationDrawerItems( + items = menuItems, + navigator = navigator, + onDismiss = { scope.launch { drawerState.close() } }, + modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding) + ) - HorizontalDivider( - modifier = Modifier.padding(NavigationDrawerDividerDefaults.ItemPadding) - ) + HorizontalDivider( + modifier = Modifier.padding(NavigationDrawerDividerDefaults.ItemPadding) + ) - NavigationDrawerTitle( - title = "Advanced", - modifier = Modifier.padding(NavigationDrawerTitleDefaults.ItemPadding) - ) + NavigationDrawerTitle( + title = "Advanced", + modifier = Modifier.padding(NavigationDrawerTitleDefaults.ItemPadding) + ) - NavigationDrawerItems( - items = advancedMenuItems, - navigator = navigator, - onDismiss = { scope.launch { drawerState.close() } }, - modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding) - ) + NavigationDrawerItems( + items = advancedMenuItems, + navigator = navigator, + onDismiss = { scope.launch { drawerState.close() } }, + modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding) + ) - NavigationDrawerItem( - icon = { - Icon( - Icons.Filled.BrokenImage, - contentDescription = null - ) - }, - label = { Text("This does nothing") }, - selected = false, - onClick = { }, - modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding) - ) + NavigationDrawerItem( + icon = { + Icon( + Icons.Filled.BrokenImage, + contentDescription = null + ) + }, + label = { Text("This does nothing") }, + selected = false, + onClick = { }, + modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding) + ) + } } } ) { From d00c06e830102c9ae6c588b922057e7e8f702d1a Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 19 Jun 2024 15:46:38 +0200 Subject: [PATCH 26/27] Enabling edge to edge in NordicActivity --- .../android/common/theme/NordicActivity.kt | 39 +++---------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/theme/src/main/java/no/nordicsemi/android/common/theme/NordicActivity.kt b/theme/src/main/java/no/nordicsemi/android/common/theme/NordicActivity.kt index 80bd7d56..2c8a4bb9 100644 --- a/theme/src/main/java/no/nordicsemi/android/common/theme/NordicActivity.kt +++ b/theme/src/main/java/no/nordicsemi/android/common/theme/NordicActivity.kt @@ -31,15 +31,13 @@ package no.nordicsemi.android.common.theme -import android.content.res.Configuration +import android.graphics.Color import android.os.Build import android.os.Bundle -import android.view.View -import android.view.WindowInsetsController import androidx.activity.ComponentActivity -import androidx.core.content.ContextCompat +import androidx.activity.SystemBarStyle +import androidx.activity.enableEdgeToEdge import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen -import androidx.core.view.WindowCompat /** * Base activity that sets the Nordic theme and the Splash Screen. @@ -57,20 +55,9 @@ abstract class NordicActivity : ComponentActivity() { setTheme(R.style.NordicTheme) super.onCreate(savedInstanceState) - setDecorFitsSystemWindows(true) - - val view = window.decorView - if (!isDarkMode()) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - window.insetsController?.setSystemBarsAppearance( - WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS, - WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS - ) - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val flags = view.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR - view.systemUiVisibility = flags - } - } + enableEdgeToEdge( + statusBarStyle = SystemBarStyle.dark(Color.TRANSPARENT) + ) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val splashScreen = installSplashScreen() @@ -87,18 +74,4 @@ abstract class NordicActivity : ComponentActivity() { } } } - - fun setDecorFitsSystemWindows(decorFitsSystemWindows: Boolean) { - if (!decorFitsSystemWindows) { - WindowCompat.setDecorFitsSystemWindows(window, false) - window.statusBarColor = ContextCompat.getColor(this, android.R.color.transparent) - } else { - window.statusBarColor = ContextCompat.getColor(this, R.color.statusBarColor) - } - } - - fun isDarkMode(): Boolean { - val darkModeFlag = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK - return darkModeFlag == Configuration.UI_MODE_NIGHT_YES - } } From ebafba11e4f72cb8bd499ec762939d14ea845180 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Wed, 19 Jun 2024 16:00:41 +0200 Subject: [PATCH 27/27] Showing disabled progress items as gray --- .../android/common/ui/view/ProgressItem.kt | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt index 19f80357..b329dd17 100644 --- a/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt +++ b/ui/src/main/java/no/nordicsemi/android/common/ui/view/ProgressItem.kt @@ -48,6 +48,7 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -101,10 +102,14 @@ fun ProgressItem( Spacer(modifier = Modifier.width(iconRightPadding)) - Column( - modifier = Modifier.padding(vertical = 2.dp) + CompositionLocalProvider( + LocalContentColor provides status.toTextColor() ) { - content() + Column( + modifier = Modifier.padding(vertical = 2.dp) + ) { + content() + } } } } @@ -132,23 +137,27 @@ fun StatusItem( } @Composable -private fun ProgressItemStatus.toIconColor(): Color { - return when (this) { - ProgressItemStatus.DISABLED -> MaterialTheme.colorScheme.surfaceVariant - ProgressItemStatus.WORKING -> LocalContentColor.current - ProgressItemStatus.SUCCESS -> colorResource(id = R.color.colorSuccess) - ProgressItemStatus.ERROR -> MaterialTheme.colorScheme.error - } +private fun ProgressItemStatus.toIconColor(): Color = when (this) { + ProgressItemStatus.DISABLED -> MaterialTheme.colorScheme.surfaceVariant + ProgressItemStatus.WORKING -> LocalContentColor.current + ProgressItemStatus.SUCCESS -> colorResource(id = R.color.colorSuccess) + ProgressItemStatus.ERROR -> MaterialTheme.colorScheme.error +} + +@Composable +private fun ProgressItemStatus.toTextColor(): Color = when (this) { + ProgressItemStatus.DISABLED -> LocalContentColor.current.copy(alpha = 0.38f) + ProgressItemStatus.WORKING, + ProgressItemStatus.SUCCESS, + ProgressItemStatus.ERROR -> LocalContentColor.current } @DrawableRes -private fun ProgressItemStatus.toImageRes(): Int { - return when (this) { - ProgressItemStatus.DISABLED -> R.drawable.ic_dot - ProgressItemStatus.WORKING -> R.drawable.ic_arrow_right - ProgressItemStatus.SUCCESS -> R.drawable.ic_check - ProgressItemStatus.ERROR -> R.drawable.ic_cross - } +private fun ProgressItemStatus.toImageRes(): Int = when (this) { + ProgressItemStatus.DISABLED -> R.drawable.ic_dot + ProgressItemStatus.WORKING -> R.drawable.ic_arrow_right + ProgressItemStatus.SUCCESS -> R.drawable.ic_check + ProgressItemStatus.ERROR -> R.drawable.ic_cross } @Preview(showBackground = true)