diff --git a/.gitignore b/.gitignore index 6c4c3f0c..8d6f5252 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,4 @@ Builds .idea/ *.iml /local.properties -*.apk *.DS_Store diff --git a/README.md b/README.md index d6f64ad5..e5c6cbf2 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,14 @@ Simplicity Connect includes many demos to test sample apps in the Silicon Labs G - **Health Thermometer**: Connect to a BLE hardware kit and receive the temperature data from the on-board sensor. - **Connected Lighting DMP**: Leverage the dynamic multi-protocol (DMP) sample apps to control a DMP light node from a mobile and protocol-specific switch node (Zigbee, proprietary) while keeping the light status in sync across all devices. - **Range Test**: Visualize the RSSI and other RF performance data on the mobile phone while running the Range Test sample application on a pair of Silicon Labs radio boards. -- **Motion**: Control a 3D render of a Silicon Labs Thunderboard or Dev Kit that follows the phyiscal board movements. +- **Motion**: Control a 3D render of a Silicon Labs Thunderboard or Dev Kit that follows the physical board movements. - **Environment**: Read and display the data from the on-board sensors on a Silicon Labs Thunderboard or Dev Kit. - **Wi-Fi Commissioning**: Commission a Wi-Fi device over BLE. - **Bluetooth Electronic Shelf Labels (ESL)**: Adds and commissions ESL tags to the system network by scanning the tag's QR code with the mobile device's camera and provides the user a UI to view the list commissioned tags and control them. - **Matter**: Commission and control of the Matter devices over Thread and Wi-Fi. - **Wi-Fi OTA Firmware Update**: The Wi-Fi OTA firmware update demo demonstrates how to update the SiWx91x user application firmware over Wi-Fi connection, by downloading the image from the mobile phone. - **Wi-Fi 917 Sensors**: The Wi-Fi 917 Sensor demo demonstrates to read and display sensor data from 917 Dev Kit. +- **Wi-Fi Throughput**: Wi-Fi demo feature for measuring data throughput between SiWx91x device and the mobile phone. ## Development Features Simplicity Connect helps developers create and troubleshoot Bluetooth applications running on Silicon Labs’ BLE hardware. Here’s a rundown of some example functionalities. diff --git a/build.gradle.kts b/build.gradle.kts index f65e0f89..78b37a5d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,10 +2,10 @@ plugins { id("com.android.application") version "8.4.1" apply false - id("org.jetbrains.kotlin.android") version "1.7.21" apply false - id("org.jetbrains.kotlin.android.extensions") version "1.7.21" apply false - id("org.jetbrains.kotlin.kapt") version "1.7.21" apply false - id("com.google.dagger.hilt.android") version "2.45" apply false + id("org.jetbrains.kotlin.android") version "1.9.21" apply false + id("org.jetbrains.kotlin.plugin.parcelize") version "1.9.21" apply false + id("org.jetbrains.kotlin.kapt") version "1.9.21" apply false + id("com.google.dagger.hilt.android") version "2.51.1" apply false } buildscript { diff --git a/build/Si-Connect - v3_0_0.apk b/build/Si-Connect - v3_0_0.apk new file mode 100644 index 00000000..2fcd719a --- /dev/null +++ b/build/Si-Connect - v3_0_0.apk @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3674b2cae4575ccb76b171d04e0f7312d32f35aea94b362475d52286a573af0 +size 71947367 diff --git a/gradle.properties b/gradle.properties index 9a84b0ad..a525f3cc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m -org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit @@ -18,6 +18,5 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro org.gradle.parallel=true android.enableJetifier=true android.useAndroidX=true -android.defaults.buildfeatures.buildconfig=true android.nonTransitiveRClass=false android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d96216a6..ee78cf57 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Oct 26 16:56:57 EEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 8fd4bf76..325139ca 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -1,9 +1,7 @@ -import com.android.build.api.variant.Packaging - plugins { id("com.android.application") id("org.jetbrains.kotlin.android") - id("org.jetbrains.kotlin.android.extensions") + id("org.jetbrains.kotlin.plugin.parcelize") id("org.jetbrains.kotlin.kapt") id("com.google.dagger.hilt.android") } @@ -25,7 +23,7 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } - packagingOptions { jniLibs { useLegacyPackaging = true } } + packaging { jniLibs { useLegacyPackaging = true } } buildTypes { release { @@ -36,13 +34,21 @@ android { ndk { abiFilters.add("armeabi-v7a") abiFilters.add("arm64-v8a") + abiFilters.add("x86-64") + abiFilters.add("x86") } } debug { isDebuggable = true - //renderscriptDebuggable false - //minifyEnabled true - //pseudoLocalesEnabled false + } + } + + applicationVariants.all{ + assembleProvider.get().doLast{ + copy{ + from("Builds/${rootProject.name}/${project.name}/outputs/apk/Si-Connect/release/mobile-Si-Connect-release.apk") + into ("Builds") + } } } @@ -60,23 +66,38 @@ android { create("Si-Connect") { dimension = versionDim applicationId = "com.siliconlabs.bledemo" - versionCode = 55 - versionName = "2.9.3" + versionCode = 56 + versionName = "3.0.0" } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8.toString() + jvmTarget = JavaVersion.VERSION_17.toString() + } + + kapt { + correctErrorTypes = true + } + + sourceSets { + getByName("main") { + jniLibs.srcDir("libs") + } } buildFeatures { viewBinding = true buildConfig = true + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = "1.5.7" } } @@ -86,21 +107,21 @@ dependencies { // This dependency is downloaded from the Google’s Maven repository. // Make sure you also include that repository in your project's build.gradle file. implementation("com.google.android.play:feature-delivery:2.1.0") - implementation("com.google.android.play:review:2.0.1") + implementation("com.google.android.play:review:2.0.2") implementation("com.google.android.play:app-update:2.1.0") // For Kotlin users, also import the Kotlin extensions library for Play Feature Delivery: implementation("com.google.android.play:feature-delivery-ktx:2.1.0") - implementation("com.google.android.play:review-ktx:2.0.1") + implementation("com.google.android.play:review-ktx:2.0.2") implementation("com.google.android.play:app-update-ktx:2.1.0") // androidx implementation("androidx.core:core-ktx:1.13.1") implementation("androidx.appcompat:appcompat:1.7.0") - implementation("androidx.fragment:fragment:1.8.1") + implementation("androidx.fragment:fragment:1.8.3") implementation("androidx.core:core-splashscreen:1.0.1") - implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.2") - implementation("androidx.activity:activity-ktx:1.9.0") + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.6") + implementation("androidx.activity:activity-ktx:1.9.2") implementation("com.google.android.material:material:1.12.0") // Coroutines @@ -121,13 +142,13 @@ dependencies { //implementation("com.github.PhilJay:MPAndroidChart:v3.0.3") // Navigation - implementation("androidx.navigation:navigation-fragment-ktx:2.7.7") - implementation("androidx.navigation:navigation-ui-ktx:2.7.7") - implementation("androidx.navigation:navigation-dynamic-features-fragment:2.7.7") + implementation("androidx.navigation:navigation-fragment-ktx:2.8.1") + implementation("androidx.navigation:navigation-ui-ktx:2.8.1") + implementation("androidx.navigation:navigation-dynamic-features-fragment:2.8.1") // Dependency injection - implementation("com.google.dagger:hilt-android:2.46.1") - kapt("com.google.dagger:hilt-android-compiler:2.45") + implementation("com.google.dagger:hilt-android:2.51.1") + kapt("com.google.dagger:hilt-android-compiler:2.51.1") // View binding implementation("com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.9") @@ -138,7 +159,7 @@ dependencies { // Parsing implementation("com.google.code.gson:gson:2.10.1") implementation("com.opencsv:opencsv:5.6") - implementation("androidx.activity:activity:1.9.0") + implementation("androidx.activity:activity:1.9.2") // Only used for Int.pow() method in a couple of places implementation("com.google.guava:guava:29.0-android") @@ -160,12 +181,24 @@ dependencies { implementation("androidx.camera:camera-camera2:1.3.4") implementation("androidx.camera:camera-lifecycle:1.3.4") implementation("androidx.camera:camera-view:1.3.4") - implementation("com.google.mlkit:barcode-scanning:17.2.0") - implementation("com.daimajia.swipelayout:library:1.2.0@aar") + implementation("com.google.mlkit:barcode-scanning:17.3.0") //DevKitSensor917 Demo implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.4.0") - implementation ("com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.6") + + implementation ("com.daimajia.swipelayout:library:1.2.0@aar") + + //JetPack compose + val composeBom = platform("androidx.compose:compose-bom:2024.06.00") + implementation(composeBom) + androidTestImplementation(composeBom) + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.material:material") + implementation("androidx.compose.ui:ui-tooling-preview") + implementation("androidx.activity:activity-compose") + androidTestImplementation("androidx.compose.ui:ui-test-junit4") + debugImplementation("androidx.compose.ui:ui-tooling") + } diff --git a/mobile/libs/AndroidPlatform.jar b/mobile/libs/AndroidPlatform.jar index b88b1546..4fecdaca 100644 Binary files a/mobile/libs/AndroidPlatform.jar and b/mobile/libs/AndroidPlatform.jar differ diff --git a/mobile/libs/CHIPClusterID.jar b/mobile/libs/CHIPClusterID.jar new file mode 100644 index 00000000..68fd4765 Binary files /dev/null and b/mobile/libs/CHIPClusterID.jar differ diff --git a/mobile/libs/CHIPClusters.jar b/mobile/libs/CHIPClusters.jar new file mode 100644 index 00000000..acb9f2a4 Binary files /dev/null and b/mobile/libs/CHIPClusters.jar differ diff --git a/mobile/libs/CHIPController.jar b/mobile/libs/CHIPController.jar index bc33c98e..92dd011e 100644 Binary files a/mobile/libs/CHIPController.jar and b/mobile/libs/CHIPController.jar differ diff --git a/mobile/libs/CHIPInteractionModel.jar b/mobile/libs/CHIPInteractionModel.jar new file mode 100644 index 00000000..744624be Binary files /dev/null and b/mobile/libs/CHIPInteractionModel.jar differ diff --git a/mobile/libs/OnboardingPayload.jar b/mobile/libs/OnboardingPayload.jar new file mode 100644 index 00000000..e4775b60 Binary files /dev/null and b/mobile/libs/OnboardingPayload.jar differ diff --git a/mobile/libs/libMatterJson.jar b/mobile/libs/libMatterJson.jar new file mode 100644 index 00000000..504ce3e5 Binary files /dev/null and b/mobile/libs/libMatterJson.jar differ diff --git a/mobile/libs/libMatterTlv.jar b/mobile/libs/libMatterTlv.jar new file mode 100644 index 00000000..d8d04a31 Binary files /dev/null and b/mobile/libs/libMatterTlv.jar differ diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 1a5ef942..6d02a259 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -23,6 +23,7 @@ + - + - + diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/Parsing/DescriptorParser.kt b/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/Parsing/DescriptorParser.kt index eab58569..c99153ab 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/Parsing/DescriptorParser.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/Parsing/DescriptorParser.kt @@ -1,6 +1,8 @@ package com.siliconlabs.bledemo.bluetooth.parsing import android.bluetooth.BluetoothGattDescriptor +import android.os.Build +import androidx.annotation.RequiresApi import com.siliconlabs.bledemo.utils.Converters import com.siliconlabs.bledemo.utils.StringUtils.removeWhitespaceAndCommaIfNeeded import java.lang.StringBuilder @@ -8,6 +10,7 @@ import java.util.* class DescriptorParser(val descriptor: BluetoothGattDescriptor) { + @RequiresApi(Build.VERSION_CODES.GINGERBREAD) fun getFormattedValue(): String { val uuid = descriptor.uuid val value = descriptor.value @@ -64,7 +67,7 @@ class DescriptorParser(val descriptor: BluetoothGattDescriptor) { } } - return "0x".plus(Converters.bytesToHex(value).toUpperCase(Locale.ROOT)) + return "0x".plus(Converters.bytesToHex(value).uppercase(Locale.ROOT)) } @@ -127,7 +130,7 @@ class DescriptorParser(val descriptor: BluetoothGattDescriptor) { return Converters.byteToUnsignedInt(bytes[0]).toString() } - + @RequiresApi(Build.VERSION_CODES.GINGERBREAD) private fun getReportReference(bytes: ByteArray): String { return if (bytes.size != 2) { return getUnknownValue(bytes) @@ -136,14 +139,17 @@ class DescriptorParser(val descriptor: BluetoothGattDescriptor) { val reportType = bytes[1] val result = StringBuilder() - result.append("Report ID: 0x").append(Converters.getHexValue(reportId).toUpperCase(Locale.ROOT)).append("\n") - result.append("Report Type: 0x").append(Converters.getHexValue(reportType).toUpperCase(Locale.ROOT)) + result.append("Report ID: 0x").append(Converters.getHexValue(reportId) + .uppercase(Locale.ROOT)).append("\n") + result.append("Report Type: 0x").append(Converters.getHexValue(reportType) + .uppercase(Locale.ROOT)) result.toString() } } - + + @RequiresApi(Build.VERSION_CODES.GINGERBREAD) private fun getValidRange(bytes: ByteArray): String { val size = bytes.size @@ -154,20 +160,21 @@ class DescriptorParser(val descriptor: BluetoothGattDescriptor) { result.append("Lower inclusive value: 0x") for (i in 0 until size / 2) { - result.append(Converters.getHexValue(bytes[i]).toUpperCase(Locale.ROOT)) + result.append(Converters.getHexValue(bytes[i]).uppercase(Locale.ROOT)) } result.append("\nUpper inclusive value: 0x") for (i in size / 2 until size) { - result.append(Converters.getHexValue(bytes[i]).toUpperCase(Locale.ROOT)) + result.append(Converters.getHexValue(bytes[i]).uppercase(Locale.ROOT)) } return result.toString() } } + @RequiresApi(Build.VERSION_CODES.GINGERBREAD) private fun getUnknownValue(bytes: ByteArray): String { - return "Unknown value: 0x".plus(Converters.bytesToHex(bytes).toUpperCase(Locale.ROOT)) + return "Unknown value: 0x".plus(Converters.bytesToHex(bytes).uppercase(Locale.ROOT)) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/Services/BluetoothService.kt b/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/Services/BluetoothService.kt index 8537a0ae..11345fe0 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/Services/BluetoothService.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/Services/BluetoothService.kt @@ -5,9 +5,21 @@ import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent -import android.bluetooth.* +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCallback +import android.bluetooth.BluetoothGattCharacteristic +import android.bluetooth.BluetoothGattDescriptor +import android.bluetooth.BluetoothGattServer +import android.bluetooth.BluetoothGattServerCallback +import android.bluetooth.BluetoothGattService +import android.bluetooth.BluetoothManager +import android.bluetooth.BluetoothProfile import android.bluetooth.le.ScanCallback -import android.bluetooth.le.ScanCallback.* +import android.bluetooth.le.ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED +import android.bluetooth.le.ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED +import android.bluetooth.le.ScanCallback.SCAN_FAILED_INTERNAL_ERROR import android.bluetooth.le.ScanFilter import android.bluetooth.le.ScanSettings import android.content.BroadcastReceiver @@ -22,19 +34,31 @@ import android.os.Looper import android.widget.Toast import androidx.core.location.LocationManagerCompat import com.siliconlabs.bledemo.R -import com.siliconlabs.bledemo.bluetooth.ble.* +import com.siliconlabs.bledemo.bluetooth.ble.BleScanCallback +import com.siliconlabs.bledemo.bluetooth.ble.BluetoothDeviceInfo +import com.siliconlabs.bledemo.bluetooth.ble.BluetoothScanCallback +import com.siliconlabs.bledemo.bluetooth.ble.ConnectedDeviceInfo +import com.siliconlabs.bledemo.bluetooth.ble.GattConnection +import com.siliconlabs.bledemo.bluetooth.ble.ScanResultCompat +import com.siliconlabs.bledemo.bluetooth.ble.TimeoutGattCallback import com.siliconlabs.bledemo.features.configure.advertiser.activities.PendingServerConnectionActivity import com.siliconlabs.bledemo.features.configure.advertiser.services.AdvertiserService import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.BluetoothGattServicesCreator import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.GattConfiguratorStorage -import com.siliconlabs.bledemo.features.scan.browser.models.logs.* +import com.siliconlabs.bledemo.features.scan.browser.models.logs.ConnectionStateChangeLog +import com.siliconlabs.bledemo.features.scan.browser.models.logs.GattOperationLog +import com.siliconlabs.bledemo.features.scan.browser.models.logs.GattOperationWithDataLog +import com.siliconlabs.bledemo.features.scan.browser.models.logs.GattOperationWithParameterLog +import com.siliconlabs.bledemo.features.scan.browser.models.logs.Log +import com.siliconlabs.bledemo.features.scan.browser.models.logs.TimeoutLog import com.siliconlabs.bledemo.home_screen.activities.MainActivity import com.siliconlabs.bledemo.utils.LocalService import com.siliconlabs.bledemo.utils.Notifications import com.siliconlabs.bledemo.utils.UuidConsts import timber.log.Timber import java.lang.reflect.Method -import java.util.* +import java.util.LinkedList +import java.util.UUID /** * Service handling Bluetooth (regular and BLE) communications. @@ -81,7 +105,8 @@ class BluetoothService : LocalService() { ESL_DEMO, MATTER_DEMO, WIFI_OTA_UPDATE, - DEV_KIT_SENSOR + DEV_KIT_SENSOR, + WIFI_THROUGHPUT_TEST } interface ScanListener { @@ -211,6 +236,7 @@ class BluetoothService : LocalService() { interface ServicesStateListener { fun onBluetoothStateChanged(isOn: Boolean) fun onLocationStateChanged(isOn: Boolean) + fun onNotificationStateChanged(isOn: Boolean) } private val bluetoothReceiver: BroadcastReceiver = object : BroadcastReceiver() { @@ -251,6 +277,10 @@ class BluetoothService : LocalService() { return LocationManagerCompat.isLocationEnabled(locationManager) } + fun areNotificationOn() : Boolean { + return (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).areNotificationsEnabled() + } + fun initGattServer() { if (bluetoothGattServer == null && bluetoothAdapter?.isEnabled == true && areBluetoothPermissionsEnabled) { bluetoothGattServer = bluetoothManager.openGattServer(this, bluetoothGattServerCallback) @@ -858,7 +888,7 @@ class BluetoothService : LocalService() { .setContentTitle(getString(R.string.notification_title_device_has_connected, deviceName)) .setContentText(getString(R.string.notification_note_debug_connection)) .addAction(buildAction(getString(R.string.button_yes), getYesPendingIntent(device))) - .addAction(buildAction(getString(R.string.notification_button_yes_and_open), getYesAndOpenPendingIntent(device))) + /* .addAction(buildAction(getString(R.string.notification_button_yes_and_open), getYesAndOpenPendingIntent(device)))*/ .addAction(buildAction(getString(R.string.button_no), getNoPendingIntent())) .build() diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/beacon_utils/BleFormat.kt b/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/beacon_utils/BleFormat.kt index f39141d4..a195ed13 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/beacon_utils/BleFormat.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/beacon_utils/BleFormat.kt @@ -2,6 +2,7 @@ package com.siliconlabs.bledemo.bluetooth.beacon_utils import com.siliconlabs.bledemo.bluetooth.ble.BluetoothDeviceInfo import com.siliconlabs.bledemo.R +import java.util.Locale enum class BleFormat(val nameResId: Int, val iconResId: Int) { UNSPECIFIED(R.string.unspecified, R.drawable.ic_beacon_immediate), @@ -66,7 +67,7 @@ enum class BleFormat(val nameResId: Int, val iconResId: Int) { val uuidList = deviceInfo.scanInfo?.scanRecord?.serviceUuids if (uuidList != null && !uuidList.isEmpty()) { for (parcelUuid in uuidList) { - val parcelString = parcelUuid.toString().substring(4, 8).toLowerCase() + val parcelString = parcelUuid.toString().substring(4, 8).lowercase(Locale.US) if (EDDYSTONE_SERVICE_UUID == parcelString) { return true } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/beacon_utils/eddystone/Beacon.kt b/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/beacon_utils/eddystone/Beacon.kt index 6c7d38f8..f97022f3 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/beacon_utils/eddystone/Beacon.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/Bluetooth/beacon_utils/eddystone/Beacon.kt @@ -1,5 +1,7 @@ package com.siliconlabs.bledemo.bluetooth.beacon_utils.eddystone +import java.util.Locale + // Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -147,11 +149,11 @@ class Beacon(private val deviceAddress: String, var rssi: Int) { */ operator fun contains(s: String?): Boolean { return (s == null || s.isEmpty() - || deviceAddress.replace(":", "").toLowerCase().contains(s.toLowerCase()) + || deviceAddress.replace(":", "").lowercase().contains(s.lowercase(Locale.US)) || (uidStatus.uidValue != null - && uidStatus.uidValue?.toLowerCase()?.contains(s.toLowerCase())!!) + && uidStatus.uidValue?.lowercase()?.contains(s.lowercase(Locale.US))!!) || (urlStatus.urlValue != null - && urlStatus.urlValue?.toLowerCase()?.contains(s.toLowerCase())!!)) + && urlStatus.urlValue?.lowercase()?.contains(s.lowercase(Locale.US))!!)) } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/Utils/Converters.kt b/mobile/src/main/java/com/siliconlabs/bledemo/Utils/Converters.kt index f4c2e4de..012fb212 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/Utils/Converters.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/Utils/Converters.kt @@ -256,7 +256,7 @@ object Converters { } // prepend the sign up to 64 bits - var result = "" + var result:String val stringPrependExtendSign = StringBuilder(binaryString) for (i in 0 until 64 - binaryString.length) { stringPrependExtendSign.insert(0, binaryString.substring(0, 1)) @@ -357,7 +357,7 @@ object Converters { val minExponent = -8 try { - var mantissa = 0 + var mantissa:Int var exponent = 0 if (input.contains('.')) { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/Utils/FilterDeviceParams.kt b/mobile/src/main/java/com/siliconlabs/bledemo/Utils/FilterDeviceParams.kt index de9b542f..2ae7f75e 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/Utils/FilterDeviceParams.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/Utils/FilterDeviceParams.kt @@ -3,7 +3,7 @@ package com.siliconlabs.bledemo.utils import android.content.Context import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.bluetooth.beacon_utils.BleFormat -import kotlinx.android.synthetic.main.fragment_filter.* + data class FilterDeviceParams( val name: String?, diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/Utils/RemovalDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/Utils/RemovalDialog.kt index d460f1d5..1f145abd 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/Utils/RemovalDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/Utils/RemovalDialog.kt @@ -5,35 +5,40 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.StringRes +import by.kirich1409.viewbindingdelegate.viewBinding import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment -import kotlinx.android.synthetic.main.dialog_info_ok_cancel.view.* +import com.siliconlabs.bledemo.databinding.DialogInfoOkCancelBinding + abstract class RemovalDialog( @StringRes private val nameRes: Int, private val onOkClicked: () -> Unit ) : BaseDialogFragment() { - + // private val binding by viewBinding(DialogInfoOkCancelBinding::bind) + private lateinit var binding: DialogInfoOkCancelBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View = inflater - .inflate(R.layout.dialog_info_ok_cancel, container, false) - .apply { - val name = context.getString(nameRes) - tv_dialog_title.text = context.getString(R.string.dialog_title_remove, name) - tv_dialog_content.text = context.getString(R.string.dialog_description_remove, name) + ): View { + binding = DialogInfoOkCancelBinding.inflate(inflater, container, false).apply { + val name = context?.getString(nameRes) + tvDialogTitle.text = context?.getString(R.string.dialog_title_remove, name) + tvDialogContent.text = + context?.getString(R.string.dialog_description_remove, name) - btn_ok.setOnClickListener { - if (cb_dont_show_again.isChecked) { + btnOk.setOnClickListener { + if (binding.cbDontShowAgain.isChecked) { blockDisplayingRemovalDialog() } onOkClicked() dismiss() } - btn_cancel.setOnClickListener { dismiss() } + btnCancel.setOnClickListener { dismiss() } } + return binding.root + } abstract fun blockDisplayingRemovalDialog() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/Utils/UuidUtils.kt b/mobile/src/main/java/com/siliconlabs/bledemo/Utils/UuidUtils.kt index dd97246b..024e7304 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/Utils/UuidUtils.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/Utils/UuidUtils.kt @@ -14,7 +14,7 @@ object UuidUtils { } fun getUuidText(uuid: UUID): String { - val strUuid = uuid.toString().toUpperCase(Locale.getDefault()) + val strUuid = uuid.toString().uppercase(Locale.getDefault()) return if (isSig(strUuid)) { "0x" + convert128to16UUID(strUuid) } else { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/common/views/DetailsRow.kt b/mobile/src/main/java/com/siliconlabs/bledemo/common/views/DetailsRow.kt index a6aa148e..b3472223 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/common/views/DetailsRow.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/common/views/DetailsRow.kt @@ -4,17 +4,24 @@ import android.content.Context import android.view.LayoutInflater import android.widget.LinearLayout import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.list_item_details_row.view.* +import com.siliconlabs.bledemo.databinding.ListItemDetailsRowBinding + +//import kotlinx.android.synthetic.main.list_item_details_row.view.* class DetailsRow : LinearLayout { + lateinit var binding: ListItemDetailsRowBinding init { - LayoutInflater.from(context).inflate(R.layout.list_item_details_row, this) + // LayoutInflater.from(context).inflate(R.layout.list_item_details_row, this) + binding = ListItemDetailsRowBinding.inflate(LayoutInflater.from(context)) + } constructor(context: Context) : super(context) constructor(context: Context, title: String?, text: String?) : super(context) { - tv_title.text = title - tv_details.text = text +// tv_title.text = title +// tv_details.text = text + binding.tvDetails.text = text + binding.tvTitle.text = title } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/activities/AdvertiserConfigActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/activities/AdvertiserConfigActivity.kt index d5986aae..4b01bfe6 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/activities/AdvertiserConfigActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/activities/AdvertiserConfigActivity.kt @@ -12,31 +12,43 @@ import android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE import android.text.TextWatcher import android.text.style.RelativeSizeSpan import android.util.TypedValue -import android.view.* +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter -import android.widget.LinearLayout import android.widget.Spinner import androidx.constraintlayout.widget.ConstraintLayout +import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.base.activities.BaseActivity +import com.siliconlabs.bledemo.databinding.ActivityAdvertiserConfigBinding +import com.siliconlabs.bledemo.databinding.AdvertiserDataContainerBinding +import com.siliconlabs.bledemo.databinding.DataTypeItemBinding +import com.siliconlabs.bledemo.databinding.DataTypeLayoutBinding import com.siliconlabs.bledemo.features.configure.advertiser.adapters.DataTypeAdapter +import com.siliconlabs.bledemo.features.configure.advertiser.dialogs.LeaveAdvertiserConfigDialog +import com.siliconlabs.bledemo.features.configure.advertiser.dialogs.ManufacturerDataDialog +import com.siliconlabs.bledemo.features.configure.advertiser.dialogs.RemoveServicesDialog +import com.siliconlabs.bledemo.features.configure.advertiser.dialogs.Service128BitDataDialog +import com.siliconlabs.bledemo.features.configure.advertiser.dialogs.Service16BitDataDialog +import com.siliconlabs.bledemo.features.configure.advertiser.enums.AdvertisingMode +import com.siliconlabs.bledemo.features.configure.advertiser.enums.DataMode +import com.siliconlabs.bledemo.features.configure.advertiser.enums.DataType +import com.siliconlabs.bledemo.features.configure.advertiser.enums.LimitType +import com.siliconlabs.bledemo.features.configure.advertiser.enums.Phy +import com.siliconlabs.bledemo.features.configure.advertiser.models.AdvertiserData +import com.siliconlabs.bledemo.features.configure.advertiser.models.DataPacket +import com.siliconlabs.bledemo.features.configure.advertiser.models.ExtendedSettings +import com.siliconlabs.bledemo.features.configure.advertiser.models.Manufacturer +import com.siliconlabs.bledemo.features.configure.advertiser.models.Service128Bit +import com.siliconlabs.bledemo.features.configure.advertiser.models.Service16Bit import com.siliconlabs.bledemo.features.configure.advertiser.presenters.AdvertiserConfigActivityPresenter import com.siliconlabs.bledemo.features.configure.advertiser.utils.AdvertiserStorage import com.siliconlabs.bledemo.features.configure.advertiser.utils.Translator import com.siliconlabs.bledemo.features.configure.advertiser.utils.Validator -import com.siliconlabs.bledemo.base.activities.BaseActivity -import com.siliconlabs.bledemo.R -import com.siliconlabs.bledemo.features.configure.advertiser.dialogs.* -import com.siliconlabs.bledemo.features.configure.advertiser.enums.* -import com.siliconlabs.bledemo.features.configure.advertiser.models.* -import kotlinx.android.synthetic.main.advertiser_config_data.* -import kotlinx.android.synthetic.main.advertiser_config_name.* -import kotlinx.android.synthetic.main.advertiser_config_parameters.* -import kotlinx.android.synthetic.main.advertiser_config_type.* -import kotlinx.android.synthetic.main.advertiser_data_container.view.* -import kotlinx.android.synthetic.main.data_type_item.view.* -import kotlinx.android.synthetic.main.data_type_layout.* -import kotlinx.android.synthetic.main.data_type_layout.view.* -import kotlinx.android.synthetic.main.data_type_layout.view.ib_remove + class AdvertiserConfigActivity : BaseActivity(), IAdvertiserConfigActivityView { private lateinit var presenter: AdvertiserConfigActivityPresenter @@ -48,6 +60,7 @@ class AdvertiserConfigActivity : BaseActivity(), IAdvertiserConfigActivityView { private var spLegacyInitialSetup = true private var spExtendedInitialSetup = true + private lateinit var binding: ActivityAdvertiserConfigBinding companion object { const val EXTRA_ADVERTISER_ITEM = "EXTRA_ADVERTISER_ITEM" @@ -56,18 +69,26 @@ class AdvertiserConfigActivity : BaseActivity(), IAdvertiserConfigActivityView { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_advertiser_config) + binding = ActivityAdvertiserConfigBinding.inflate(LayoutInflater.from(this)) + setContentView(binding.root) prepareToolbar() resetInitialSetupFlags() - presenter = AdvertiserConfigActivityPresenter(this, AdvertiserStorage(this@AdvertiserConfigActivity)) + presenter = AdvertiserConfigActivityPresenter( + this, + AdvertiserStorage(this@AdvertiserConfigActivity) + ) presenter.prepareAdvertisingTypes() presenter.preparePhyParameters() - advertiserData = intent.extras?.getParcelable(EXTRA_ADVERTISER_ITEM) as AdvertiserData + advertiserData = + intent.extras?.getParcelable(EXTRA_ADVERTISER_ITEM) as AdvertiserData position = intent.getIntExtra(EXTRA_ITEM_POSITION, 0) - presenter.onItemReceived(advertiserData, AdvertiserStorage(this@AdvertiserConfigActivity).isAdvertisingExtensionSupported()) + presenter.onItemReceived( + advertiserData, + AdvertiserStorage(this@AdvertiserConfigActivity).isAdvertisingExtensionSupported() + ) startConfigData = advertiserData.deepCopy() @@ -94,15 +115,18 @@ class AdvertiserConfigActivity : BaseActivity(), IAdvertiserConfigActivityView { presenter.handleSave() true } + android.R.id.home -> { exitConfigView() true } + else -> super.onOptionsItemSelected(item) } } override fun onBackPressed() { + super.onBackPressed() exitConfigView() } @@ -142,15 +166,16 @@ class AdvertiserConfigActivity : BaseActivity(), IAdvertiserConfigActivityView { // 3. Verify if other data has changed startConfigData.apply { when { - name != et_advertising_set_name.text.toString() -> return true - isLegacy != rb_legacy_advertising.isChecked -> return true + + name != binding.advConfigName.etAdvertisingSetName.text.toString() -> return true + isLegacy != binding.advConfigType.rbLegacyAdvertising.isChecked -> return true mode != getAdvertisingMode() -> return true settings != getExtendedSettings() -> return true advertisingIntervalMs != getAdvertisingInterval() -> return true txPower != getTxPower() -> return true limitType != getAdvertisingLimitType() -> return true - timeLimit.toString() != et_time_limit.text.toString() -> return true - isEventLimitAvailable() && eventLimit.toString() != et_event_limit.text.toString() -> return true + timeLimit.toString() != binding.advConfigParam.etTimeLimit.text.toString() -> return true + isEventLimitAvailable() && eventLimit.toString() != binding.advConfigParam.etEventLimit.text.toString() -> return true } } @@ -160,7 +185,7 @@ class AdvertiserConfigActivity : BaseActivity(), IAdvertiserConfigActivityView { private fun handleAdvertisingSetNameChanges() { - et_advertising_set_name.addTextChangedListener(object : TextWatcher { + binding.advConfigName.etAdvertisingSetName.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { @@ -170,23 +195,32 @@ class AdvertiserConfigActivity : BaseActivity(), IAdvertiserConfigActivityView { } private fun isEventLimitAvailable(): Boolean { - return ll_event_limit.visibility == View.VISIBLE + return binding.advConfigParam.llEventLimit.visibility == View.VISIBLE } private fun isTxPowerNotValid(): Boolean { - return ll_tx_power.visibility == View.VISIBLE && !Validator.isTxPowerValid(et_tx_power.text.toString()) + + return binding.advConfigParam.llTxPower.visibility == View.VISIBLE && + !Validator.isTxPowerValid(binding.advConfigParam.etTxPower.text.toString()) } private fun isAdvertisingIntervalNotValid(): Boolean { - return ll_advertising_interval.visibility == View.VISIBLE && !Validator.isAdvertisingIntervalValid(et_advertising_interval.text.toString()) + return binding.advConfigParam.llAdvertisingInterval.visibility == View.VISIBLE && + !Validator.isAdvertisingIntervalValid( + binding.advConfigParam.etAdvertisingInterval.text.toString() + ) } private fun isTimeLimitNotValid(): Boolean { - return rb_time_limit.isChecked && !Validator.isAdvertisingTimeLimitValid(et_time_limit.text.toString(), true) + return binding.advConfigParam.rbTimeLimit.isChecked && !Validator.isAdvertisingTimeLimitValid( + binding.advConfigParam.etTimeLimit.text.toString(), + true + ) } private fun isEventLimitNotValid(): Boolean { - return rb_event_limit.isChecked && !Validator.isAdvertisingEventLimitValid(et_event_limit.text.toString()) + return binding.advConfigParam.rbEventLimit.isChecked && + !Validator.isAdvertisingEventLimitValid(binding.advConfigParam.etEventLimit.text.toString()) } private fun isAnyInputNotValid(): Boolean { @@ -203,8 +237,8 @@ class AdvertiserConfigActivity : BaseActivity(), IAdvertiserConfigActivityView { else if (isEventLimitNotValid()) showMessage(R.string.advertiser_config_message_invalid_event_limit) else { - val name = et_advertising_set_name.text.toString() - val isLegacy = rb_legacy_advertising.isChecked + val name = binding.advConfigName.etAdvertisingSetName.text.toString() + val isLegacy = binding.advConfigType.rbLegacyAdvertising.isChecked val advertisingMode = getAdvertisingMode() val settings = getExtendedSettings() val interval = getAdvertisingInterval() @@ -228,502 +262,777 @@ class AdvertiserConfigActivity : BaseActivity(), IAdvertiserConfigActivityView { } private fun getAdvertisingInterval(): Int { - return et_advertising_interval.text.toString().toInt() + return binding.advConfigParam.etAdvertisingInterval.text.toString().toInt() } private fun getTxPower(): Int { - return et_tx_power.text.toString().toInt() + return binding.advConfigParam.etTxPower.text.toString().toInt() } private fun getAdvertisingLimitType(): LimitType { return when { - rb_no_limit.isChecked -> LimitType.NO_LIMIT - rb_time_limit.isChecked -> LimitType.TIME_LIMIT + binding.advConfigParam.rbNoLimit.isChecked -> LimitType.NO_LIMIT + binding.advConfigParam.rbTimeLimit.isChecked -> LimitType.TIME_LIMIT else -> LimitType.EVENT_LIMIT } } private fun getAdvertisingTimeLimit(): Int { - return if (rb_time_limit.isChecked) et_time_limit.text.toString().toInt() + return if (binding.advConfigParam.rbTimeLimit.isChecked) binding.advConfigParam.etTimeLimit.text.toString() + .toInt() else -1 } private fun getAdvertisingEventLimit(): Int { - return if (rb_event_limit.isChecked) et_event_limit.text.toString().toInt() + return if (binding.advConfigParam.rbEventLimit.isChecked) binding.advConfigParam.etEventLimit.text.toString() + .toInt() else -1 } private fun getAdvertisingMode(): AdvertisingMode { - return if (rb_legacy_advertising.isChecked) translator.getStringAsAdvertisingMode(sp_legacy.selectedItem.toString()) - else translator.getStringAsAdvertisingMode(sp_extended.selectedItem.toString()) + + return if (binding.advConfigType.rbLegacyAdvertising.isChecked) translator.getStringAsAdvertisingMode( + binding.advConfigType.spLegacy.selectedItem.toString() + ) + else translator + .getStringAsAdvertisingMode(binding.advConfigType.spExtended.selectedItem.toString()) } private fun getExtendedSettings(): ExtendedSettings { - return if (sp_primary_phy.isEnabled) { - val primaryPhy = translator.getStringAsPhy(sp_primary_phy.selectedItem.toString()) - val secondaryPhy: Phy = translator.getStringAsPhy(sp_secondary_phy.selectedItem.toString()) - ExtendedSettings(cb_include_tx_power.isChecked, cb_anonymous.isChecked, primaryPhy, secondaryPhy) - } else ExtendedSettings(cb_include_tx_power.isChecked, cb_anonymous.isChecked, null, null) + + return if (binding.advConfigParam.spPrimaryPhy.isEnabled) { + val primaryPhy = + translator.getStringAsPhy(binding.advConfigParam.spPrimaryPhy.selectedItem.toString()) + val secondaryPhy: Phy = + translator.getStringAsPhy(binding.advConfigParam.spSecondaryPhy.selectedItem.toString()) + ExtendedSettings( + binding.advConfigType.cbIncludeTxPower.isChecked, + binding.advConfigType.cbAnonymous.isChecked, + primaryPhy, + secondaryPhy + ) + } else ExtendedSettings( + binding.advConfigType.cbIncludeTxPower.isChecked, + binding.advConfigType.cbAnonymous.isChecked, + null, + null + ) } private fun handleAdvertisingLimitSelection() { - rb_no_limit.setOnClickListener { - rb_no_limit.isChecked = true + binding.advConfigParam.rbNoLimit.setOnClickListener { + binding.advConfigParam.rbNoLimit.isChecked = true setTimeLimitState(false) setEventLimitState(false) } - rb_time_limit.setOnClickListener { - rb_no_limit.isChecked = false + binding.advConfigParam.rbTimeLimit.setOnClickListener { + binding.advConfigParam.rbNoLimit.isChecked = false setTimeLimitState(true) setEventLimitState(false) } - rb_event_limit.setOnClickListener { - rb_no_limit.isChecked = false + binding.advConfigParam.rbEventLimit.setOnClickListener { + binding.advConfigParam.rbNoLimit.isChecked = false setTimeLimitState(false) setEventLimitState(true) } } private fun setTimeLimitState(enabled: Boolean) { - rb_time_limit.isChecked = enabled - et_time_limit.isEnabled = enabled + binding.advConfigParam.rbTimeLimit.isChecked = enabled + binding.advConfigParam.etTimeLimit.isEnabled = enabled } private fun setEventLimitState(enabled: Boolean) { - rb_event_limit.isChecked = enabled - et_event_limit.isEnabled = enabled + binding.advConfigParam.rbEventLimit.isChecked = enabled + binding.advConfigParam.etEventLimit.isEnabled = enabled } - private fun updateAdvertisingLimitLabels(){ - rb_time_limit.text = buildLabelWithResizedHint(getString(R.string.advertiser_label_time_limit)) - rb_event_limit.text = buildLabelWithResizedHint(getString(R.string.advertiser_label_event_limit)) + private fun updateAdvertisingLimitLabels() { + binding.advConfigParam.rbTimeLimit.text = + buildLabelWithResizedHint(getString(R.string.advertiser_label_time_limit)) + binding.advConfigParam.rbEventLimit.text = + buildLabelWithResizedHint(getString(R.string.advertiser_label_event_limit)) } private fun buildLabelWithResizedHint(labelText: String): SpannableString { val linebreakIndex = labelText.indexOf('\n') val label = SpannableString(labelText) if (linebreakIndex > -1) { - label.setSpan(RelativeSizeSpan(12f/14f), linebreakIndex + 1, labelText.length, SPAN_EXCLUSIVE_EXCLUSIVE) + label.setSpan( + RelativeSizeSpan(12f / 14f), + linebreakIndex + 1, + labelText.length, + SPAN_EXCLUSIVE_EXCLUSIVE + ) } return label } - override fun onAdvertisingTypesPrepared(isLegacy: Boolean, legacyModes: List, extendedModes: List) { - val legacyAdapter = ArrayAdapter(this, R.layout.spinner_item_layout_medium, translator.getValuesAsStringList(legacyModes)) + override fun onAdvertisingTypesPrepared( + isLegacy: Boolean, + legacyModes: List, + extendedModes: List + ) { + val legacyAdapter = ArrayAdapter( + this, + R.layout.spinner_item_layout_medium, + translator.getValuesAsStringList(legacyModes) + ) + legacyAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item_layout) - sp_legacy.adapter = legacyAdapter + binding.advConfigType.spLegacy.adapter = legacyAdapter - val extendedAdapter = ArrayAdapter(this, R.layout.spinner_item_layout_medium, translator.getValuesAsStringList(extendedModes)) + val extendedAdapter = ArrayAdapter( + this, + R.layout.spinner_item_layout_medium, + translator.getValuesAsStringList(extendedModes) + ) extendedAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item_layout) - sp_extended.adapter = extendedAdapter + binding.advConfigType.spExtended.adapter = extendedAdapter - if (isLegacy) tv_extended_adv_not_supported.visibility = View.VISIBLE - sp_extended.isEnabled = false - cb_anonymous.isEnabled = false - cb_include_tx_power.isEnabled = false - rb_extended_advertising.isEnabled = !isLegacy - sp_primary_phy.isEnabled = false - sp_secondary_phy.isEnabled = false + if (isLegacy) binding.advConfigType.tvExtendedAdvNotSupported.visibility = View.VISIBLE + binding.advConfigType.spExtended.isEnabled = false + binding.advConfigType.cbAnonymous.isEnabled = false + binding.advConfigType.cbIncludeTxPower.isEnabled = false + binding.advConfigType.rbExtendedAdvertising.isEnabled = !isLegacy + binding.advConfigParam.spPrimaryPhy.isEnabled = false + binding.advConfigParam.spSecondaryPhy.isEnabled = false if (!isLegacy) { - rb_extended_advertising.setOnCheckedChangeListener { _, isChecked -> + binding.advConfigType.rbExtendedAdvertising.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { - cb_anonymous.isEnabled = sp_extended.selectedItem.toString() == getString(R.string.advertiser_mode_non_connectable_non_scannable) - rb_legacy_advertising.isChecked = false - sp_legacy.isEnabled = false - sp_legacy.visibility = View.GONE - sp_extended.isEnabled = true - sp_extended.visibility = View.VISIBLE - cb_include_tx_power.isEnabled = true - sp_primary_phy.isEnabled = true - sp_secondary_phy.isEnabled = true - - presenter.setSupportedData(false, translator.getStringAsAdvertisingMode(sp_extended.selectedItem.toString())) + binding.advConfigType.cbAnonymous.isEnabled = + binding.advConfigType.spExtended.selectedItem.toString() == getString(R.string.advertiser_mode_non_connectable_non_scannable) + binding.advConfigType.rbLegacyAdvertising.isChecked = false + binding.advConfigType.spLegacy.isEnabled = false + binding.advConfigType.spLegacy.visibility = View.GONE + binding.advConfigType.spExtended.isEnabled = true + binding.advConfigType.spExtended.visibility = View.VISIBLE + binding.advConfigType.cbIncludeTxPower.isEnabled = true + binding.advConfigParam.spPrimaryPhy.isEnabled = true + binding.advConfigParam.spSecondaryPhy.isEnabled = true + + presenter.setSupportedData( + false, + translator.getStringAsAdvertisingMode(binding.advConfigType.spExtended.selectedItem.toString()) + ) } } - sp_extended.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(parent: AdapterView<*>?) {} - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - if (!spExtendedInitialSetup) { - if (sp_extended.selectedItem.toString() == getString(R.string.advertiser_mode_non_connectable_non_scannable)) { - cb_anonymous.isEnabled = true + binding.advConfigType.spExtended.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) {} + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (!spExtendedInitialSetup) { + if (binding.advConfigType.spExtended.selectedItem.toString() == getString( + R.string.advertiser_mode_non_connectable_non_scannable + ) + ) { + binding.advConfigType.cbAnonymous.isEnabled = true + } else { + binding.advConfigType.cbAnonymous.isEnabled = false + binding.advConfigType.cbAnonymous.isChecked = false + } + presenter.setSupportedData( + false, + translator.getStringAsAdvertisingMode(binding.advConfigType.spExtended.selectedItem.toString()) + ) } else { - cb_anonymous.isEnabled = false - cb_anonymous.isChecked = false + spExtendedInitialSetup = false } - presenter.setSupportedData(false, translator.getStringAsAdvertisingMode(sp_extended.selectedItem.toString())) - } else { - spExtendedInitialSetup = false } } - } } - rb_legacy_advertising.setOnCheckedChangeListener { _, isChecked -> + binding.advConfigType.rbLegacyAdvertising.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { - rb_extended_advertising.isChecked = false - sp_legacy.isEnabled = true - sp_legacy.visibility = View.VISIBLE - sp_extended.isEnabled = false - sp_extended.visibility = View.GONE - cb_anonymous.isEnabled = false - cb_include_tx_power.isEnabled = false - sp_primary_phy.isEnabled = false - sp_secondary_phy.isEnabled = false - cb_include_tx_power.isChecked = false - cb_anonymous.isChecked = false - - presenter.setSupportedData(true, translator.getStringAsAdvertisingMode(sp_legacy.selectedItem.toString())) + binding.advConfigType.rbExtendedAdvertising.isChecked = false + binding.advConfigType.spLegacy.isEnabled = true + binding.advConfigType.spLegacy.visibility = View.VISIBLE + binding.advConfigType.spExtended.isEnabled = false + binding.advConfigType.spExtended.visibility = View.GONE + binding.advConfigType.cbAnonymous.isEnabled = false + binding.advConfigType.cbIncludeTxPower.isEnabled = false + binding.advConfigParam.spPrimaryPhy.isEnabled = false + binding.advConfigParam.spSecondaryPhy.isEnabled = false + binding.advConfigType.cbIncludeTxPower.isChecked = false + binding.advConfigType.cbAnonymous.isChecked = false + + presenter.setSupportedData( + true, + translator.getStringAsAdvertisingMode(binding.advConfigType.spLegacy.selectedItem.toString()) + ) } } - sp_legacy.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(parent: AdapterView<*>?) {} - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - if (!spLegacyInitialSetup) { - presenter.setSupportedData(true, translator.getStringAsAdvertisingMode(sp_legacy.selectedItem.toString())) - } else { - spLegacyInitialSetup = false + binding.advConfigType.spLegacy.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) {} + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (!spLegacyInitialSetup) { + presenter.setSupportedData( + true, + translator.getStringAsAdvertisingMode(binding.advConfigType.spLegacy.selectedItem.toString()) + ) + } else { + spLegacyInitialSetup = false + } } } - } } - override fun onAdvertisingParametersPrepared(isLegacy: Boolean, primaryPhys: List, secondaryPhys: List) { - val primaryPhyAdapter = ArrayAdapter(this, R.layout.spinner_item_layout_medium, translator.getValuesAsStringList(primaryPhys)) + override fun onAdvertisingParametersPrepared( + isLegacy: Boolean, + primaryPhys: List, + secondaryPhys: List + ) { + binding.advConfigType + val primaryPhyAdapter = ArrayAdapter( + this, + R.layout.spinner_item_layout_medium, + translator.getValuesAsStringList(primaryPhys) + ) primaryPhyAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item_layout) - sp_primary_phy.adapter = primaryPhyAdapter + binding.advConfigParam.spPrimaryPhy.adapter = primaryPhyAdapter - val secondaryPhyAdapter = ArrayAdapter(this, R.layout.spinner_item_layout_medium, translator.getValuesAsStringList(secondaryPhys)) + val secondaryPhyAdapter = ArrayAdapter( + this, + R.layout.spinner_item_layout_medium, + translator.getValuesAsStringList(secondaryPhys) + ) secondaryPhyAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item_layout) - sp_secondary_phy.adapter = secondaryPhyAdapter + binding.advConfigParam.spSecondaryPhy.adapter = secondaryPhyAdapter } override fun onSupportedDataPrepared(isAdvertisingData: Boolean, isScanRespData: Boolean) { - btn_add_advertising_data.isEnabled = isAdvertisingData - tv_adv_data_available_bytes.visibility = if (isAdvertisingData) View.VISIBLE else View.GONE - ll_data_advertising_data.visibility = if (isAdvertisingData) View.VISIBLE else View.GONE - btn_add_scan_response_data.isEnabled = isScanRespData - tv_scan_resp_available_bytes.visibility = if (isScanRespData) View.VISIBLE else View.GONE - ll_data_scan_resp_data.visibility = if (isScanRespData) View.VISIBLE else View.GONE + + binding.advConfigData.btnAddAdvertisingData.isEnabled = isAdvertisingData + binding.advConfigData.tvAdvDataAvailableBytes.visibility = + if (isAdvertisingData) View.VISIBLE else View.GONE + binding.advConfigData.llDataAdvertisingData.root.visibility = + if (isAdvertisingData) View.VISIBLE else View.GONE + binding.advConfigData.btnAddScanResponseData.isEnabled = isScanRespData + binding.advConfigData.tvScanRespAvailableBytes.visibility = + if (isScanRespData) View.VISIBLE else View.GONE + binding.advConfigData.llDataScanRespData.root.visibility = + if (isScanRespData) View.VISIBLE else View.GONE } override fun populateUi(data: AdvertiserData, isAdvertisingEventSupported: Boolean) { - et_advertising_set_name.setText(data.name) + binding.advConfigName.etAdvertisingSetName.setText(data.name) title = data.name data.isLegacy.let { - if (data.isLegacy) setSpinnerSelection(data.mode, sp_legacy) - else setSpinnerSelection(data.mode, sp_extended) - rb_legacy_advertising.isChecked = data.isLegacy - rb_extended_advertising.isChecked = !data.isLegacy + if (data.isLegacy) setSpinnerSelection(data.mode, binding.advConfigType.spLegacy) + else setSpinnerSelection(data.mode, binding.advConfigType.spExtended) + binding.advConfigType.rbLegacyAdvertising.isChecked = data.isLegacy + binding.advConfigType.rbExtendedAdvertising.isChecked = !data.isLegacy } data.settings.let { - cb_include_tx_power.isChecked = data.settings.includeTxPower - cb_anonymous.isChecked = data.settings.anonymous - setSpinnerSelection(data.settings.primaryPhy, sp_primary_phy) - setSpinnerSelection(data.settings.secondaryPhy, sp_secondary_phy) + binding.advConfigType.cbIncludeTxPower.isChecked = data.settings.includeTxPower + binding.advConfigType.cbAnonymous.isChecked = data.settings.anonymous + setSpinnerSelection(data.settings.primaryPhy, binding.advConfigParam.spPrimaryPhy) + setSpinnerSelection(data.settings.secondaryPhy, binding.advConfigParam.spSecondaryPhy) } - et_advertising_interval.setText(data.advertisingIntervalMs.toString()) - et_tx_power.setText(data.txPower.toString()) + binding.advConfigParam.etAdvertisingInterval.setText(data.advertisingIntervalMs.toString()) + binding.advConfigParam.etTxPower.setText(data.txPower.toString()) - rb_time_limit.text = getString(R.string.advertiser_label_time_limit) + binding.advConfigParam.rbTimeLimit.text = getString(R.string.advertiser_label_time_limit) - rb_no_limit.isChecked = data.limitType == LimitType.NO_LIMIT + binding.advConfigParam.rbNoLimit.isChecked = data.limitType == LimitType.NO_LIMIT setTimeLimitState(data.limitType == LimitType.TIME_LIMIT) setEventLimitState(data.limitType == LimitType.EVENT_LIMIT) - et_time_limit.setText(data.timeLimit.toString()) + binding.advConfigParam.etTimeLimit.setText(data.timeLimit.toString()) - if (isAdvertisingEventSupported) et_event_limit.setText(data.eventLimit.toString()) - else ll_event_limit.visibility = View.GONE + if (isAdvertisingEventSupported) binding.advConfigParam.etEventLimit.setText(data.eventLimit.toString()) + else binding.advConfigParam.llEventLimit.visibility = View.GONE } private fun setSpinnerSelection(selection: Any?, spinner: Spinner) { - for (i in 0 until spinner.adapter.count) if (spinner.getItemAtPosition(i) == translator.getString(selection)) { - spinner.setSelection(i); break - } + for (i in 0 until spinner.adapter.count) + if (spinner.getItemAtPosition(i) == translator.getString(selection) + ) { + spinner.setSelection(i); break + } } private fun loadData() { presenter.loadData(DataMode.ADVERTISING_DATA) presenter.loadData(DataMode.SCAN_RESPONSE_DATA) - handleAddData(ll_data_advertising_data.findViewById(R.id.data_flags), DataType.FLAGS, DataMode.ADVERTISING_DATA) + + handleAddData( + binding.advConfigData.llDataAdvertisingData.dataFlags, + DataType.FLAGS, + DataMode.ADVERTISING_DATA + ) } private fun prepareDataSpinners() { - prepareDataSpinner(sp_advertising_data, ll_data_advertising_data, DataMode.ADVERTISING_DATA) - prepareDataSpinner(sp_scan_response_data, ll_data_scan_resp_data, DataMode.SCAN_RESPONSE_DATA) - btn_add_advertising_data.setOnClickListener { sp_advertising_data.performClick() } - btn_add_scan_response_data.setOnClickListener { sp_scan_response_data.performClick() } + prepareDataSpinner( + binding.advConfigData.spAdvertisingData, + binding.advConfigData.llDataAdvertisingData, + DataMode.ADVERTISING_DATA + ) + prepareDataSpinner( + binding.advConfigData.spScanResponseData, + binding.advConfigData.llDataScanRespData, + DataMode.SCAN_RESPONSE_DATA + ) + binding.advConfigData.btnAddAdvertisingData.setOnClickListener { + binding.advConfigData.spAdvertisingData.performClick() + } + binding.advConfigData.btnAddScanResponseData.setOnClickListener { + binding.advConfigData.spScanResponseData.performClick() + } } override fun onDataLoaded(data: DataPacket?, mode: DataMode) { - val dataContainer = if (mode == DataMode.ADVERTISING_DATA) ll_data_advertising_data else ll_data_scan_resp_data + val dataContainer = + if (mode == DataMode.ADVERTISING_DATA) { + binding.advConfigData.llDataAdvertisingData + } else { + binding.advConfigData.llDataScanRespData + } data?.let { if (data.includeCompleteLocalName) - handleAddData(dataContainer.findViewById(R.id.data_complete_local_name), DataType.COMPLETE_LOCAL_NAME, mode) + handleAddData( + dataContainer.dataCompleteLocalName, + DataType.COMPLETE_LOCAL_NAME, + mode + ) if (data.manufacturers.isNotEmpty()) - for (item in data.manufacturers) handleAddData(dataContainer.findViewById(R.id.data_manufacturer_data), DataType.MANUFACTURER_SPECIFIC_DATA, mode, item) + for (item in data.manufacturers) handleAddData( + dataContainer.dataManufacturerData, + DataType.MANUFACTURER_SPECIFIC_DATA, + mode, + item + ) if (data.includeTxPower) - handleAddData(dataContainer.findViewById(R.id.data_tx_power), DataType.TX_POWER, mode) + handleAddData( + dataContainer.dataTxPower, + DataType.TX_POWER, + mode + ) if (data.services16Bit.isNotEmpty()) - handleAddData(dataContainer.findViewById(R.id.data_16bit_services), DataType.COMPLETE_16_BIT, mode, data.services16Bit) + handleAddData( + + dataContainer.data16bitServices, + DataType.COMPLETE_16_BIT, + mode, + data.services16Bit + ) if (data.services128Bit.isNotEmpty()) - handleAddData(dataContainer.findViewById(R.id.data_128bit_services), DataType.COMPLETE_128_BIT, mode, data.services128Bit) + handleAddData( + //dataContainer.findViewById(R.id.data_128bit_services), + dataContainer.data128bitServices, + DataType.COMPLETE_128_BIT, + mode, + data.services128Bit + ) } } - private fun prepareDataSpinner(spinner: Spinner, dataContainer: View, mode: DataMode) { + private fun prepareDataSpinner(spinner: Spinner, dataContainer: AdvertiserDataContainerBinding, mode: DataMode) { spinner.setSelection(0) - spinner.adapter = DataTypeAdapter(this, translator.getAdvertisingDataTypes(), object : DataTypeAdapter.Callback { - override fun onItemClick(position: Int) { - when (position) { - 0 -> handleAddData(dataContainer.findViewById(R.id.data_complete_local_name), DataType.COMPLETE_LOCAL_NAME, mode) - 1 -> { - currentFocus?.clearFocus() - hideKeyboard() - handleAddData(dataContainer.findViewById(R.id.data_manufacturer_data), DataType.MANUFACTURER_SPECIFIC_DATA, mode) + spinner.adapter = DataTypeAdapter( + this, + translator.getAdvertisingDataTypes(), + object : DataTypeAdapter.Callback { + override fun onItemClick(position: Int) { + when (position) { + 0 -> handleAddData( + //dataContainer.findViewById(R.id.data_complete_local_name), + dataContainer.dataCompleteLocalName, + DataType.COMPLETE_LOCAL_NAME, + mode + ) + + 1 -> { + currentFocus?.clearFocus() + hideKeyboard() + handleAddData( + //dataContainer.findViewById(R.id.data_manufacturer_data), + dataContainer.dataManufacturerData, + DataType.MANUFACTURER_SPECIFIC_DATA, + mode + ) + } + + 2 -> handleAddData(dataContainer.dataTxPower, DataType.TX_POWER, mode) + 3 -> handleAddData( + dataContainer.data16bitServices, + DataType.COMPLETE_16_BIT, + mode + ) + + 4 -> handleAddData( + dataContainer.data128bitServices, + DataType.COMPLETE_128_BIT, + mode + ) } - 2 -> handleAddData(dataContainer.data_tx_power, DataType.TX_POWER, mode) - 3 -> handleAddData(dataContainer.data_16bit_services, DataType.COMPLETE_16_BIT, mode) - 4 -> handleAddData(dataContainer.data_128bit_services, DataType.COMPLETE_128_BIT, mode) } - } - }) + }) } - private fun handleAddData(layout: ViewGroup, type: DataType, mode: DataMode, extra: Any? = null) { + private fun handleAddData( + layout: ViewGroup, + type: DataType, + mode: DataMode, + extra: Any? = null + ) { val baseContainer = prepareBaseContainer(layout, type) when (type) { DataType.FLAGS -> addFlagsData(layout, baseContainer) - DataType.COMPLETE_16_BIT -> addServiceData(layout, baseContainer, mode, DataType.COMPLETE_16_BIT, extra) - DataType.COMPLETE_128_BIT -> addServiceData(layout, baseContainer, mode, DataType.COMPLETE_128_BIT, extra) - DataType.COMPLETE_LOCAL_NAME -> addCompleteLocalNameData(layout, baseContainer, mode) + DataType.COMPLETE_16_BIT -> addServiceData( + layout, + baseContainer, + mode, + DataType.COMPLETE_16_BIT, + extra + ) + + DataType.COMPLETE_128_BIT -> addServiceData( + layout, + baseContainer, + mode, + DataType.COMPLETE_128_BIT, + extra + ) + + DataType.COMPLETE_LOCAL_NAME -> addCompleteLocalNameData( + layout, + baseContainer, + mode + ) + DataType.TX_POWER -> addTxPowerData(layout, baseContainer, mode) - DataType.MANUFACTURER_SPECIFIC_DATA -> addManufacturerSpecificData(layout, baseContainer, mode, extra) + DataType.MANUFACTURER_SPECIFIC_DATA -> addManufacturerSpecificData( + layout, + baseContainer, + mode, + extra + ) } } - private fun addFlagsData(layout: ViewGroup, baseContainer: View) { - baseContainer.ib_remove.visibility = View.INVISIBLE - val itemContainer = prepareItemContainer(baseContainer, getString(R.string.advertiser_label_flags_default)) - itemContainer.ib_remove.visibility = View.GONE - baseContainer.ll_data.addView(itemContainer) - layout.addView(baseContainer) + private fun addFlagsData(layout: ViewGroup, baseContainer: DataTypeLayoutBinding) { + baseContainer.ibRemove.visibility = View.INVISIBLE + val itemContainer = + prepareItemContainer(baseContainer, getString(R.string.advertiser_label_flags_default)) + itemContainer.ibRemove.visibility = View.GONE + baseContainer.llData.addView(itemContainer.root) + layout.addView(baseContainer.root) } - private fun addServiceData(layout: ViewGroup, baseContainer: View, mode: DataMode, type: DataType, extra: Any? = null) { + private fun addServiceData( + layout: ViewGroup, + baseContainer: DataTypeLayoutBinding, + mode: DataMode, + type: DataType, + extra: Any? = null + ) { baseContainer.apply { - ib_remove.setOnClickListener { layout.removeView(baseContainer) } - btn_add_service.apply { + ibRemove.setOnClickListener { layout.removeView(baseContainer.root) } + btnAddService.apply { visibility = View.VISIBLE - text = if (type == DataType.COMPLETE_16_BIT) getString(R.string.advertiser_button_add_16bit_service) else getString(R.string.advertiser_button_add_128bit_service) + text = + if (type == DataType.COMPLETE_16_BIT) getString(R.string.advertiser_button_add_16bit_service) else getString( + R.string.advertiser_button_add_128bit_service + ) } - ll_data_spacer.visibility = View.VISIBLE - (ll_data.layoutParams as ConstraintLayout.LayoutParams).apply { - endToEnd = ib_remove.id - topToBottom = ib_remove.id + llDataSpacer.visibility = View.VISIBLE + (llData.layoutParams as ConstraintLayout.LayoutParams).apply { + endToEnd = ibRemove.id + topToBottom = ibRemove.id } - ll_data.requestLayout() + llData.requestLayout() setDataSpinnerItemState(false, mode, if (type == DataType.COMPLETE_16_BIT) 3 else 4) - btn_add_service.setOnClickListener { + btnAddService.setOnClickListener { currentFocus?.clearFocus() hideKeyboard() if (type == DataType.COMPLETE_16_BIT) { Service16BitDataDialog(object : Service16BitDataDialog.Callback { override fun onSave(service: Service16Bit) { - val serviceItem = prepareItemContainer(baseContainer, service.toString()) + val serviceItem = + prepareItemContainer(baseContainer, service.toString()) presenter.include16BitService(mode, service) - serviceItem.ib_remove.setOnClickListener { + serviceItem.ibRemove.setOnClickListener { presenter.exclude16BitService(mode, service) - ll_data.removeView(serviceItem) + llData.removeView(serviceItem.root) } - ll_data.addView(serviceItem) + llData.addView(serviceItem.root) } }).show(supportFragmentManager, "dialog_16bit_service_data") } else { Service128BitDataDialog(object : Service128BitDataDialog.Callback { override fun onSave(service: Service128Bit) { - val serviceItem = prepareItemContainer(baseContainer, service.uuid.toString()) + val serviceItem = + prepareItemContainer(baseContainer, service.uuid.toString()) presenter.include128BitService(mode, service) - serviceItem.ib_remove.setOnClickListener { + serviceItem.ibRemove.setOnClickListener { presenter.exclude128BitService(mode, service) - ll_data.removeView(serviceItem) + llData.removeView(serviceItem.root) } - ll_data.addView(serviceItem) + llData.addView(serviceItem.root) } }).show(supportFragmentManager, "dialog_128bit_service_data") } } - ib_remove.setOnClickListener { + ibRemove.setOnClickListener { currentFocus?.clearFocus() hideKeyboard() - val count = ll_data.childCount + val count = llData.childCount removeServicesIfAllowed(layout, baseContainer, count, mode, type) } extra?.let { for (service in extra as List<*>) { - val serviceItem = prepareItemContainer(baseContainer, if (service is Service16Bit) service.toString() else (service as Service128Bit).uuid.toString()) + val serviceItem = prepareItemContainer( + baseContainer, + if (service is Service16Bit) service.toString() else (service as Service128Bit).uuid.toString() + ) - serviceItem.ib_remove.setOnClickListener { + serviceItem.ibRemove.setOnClickListener { if (service is Service16Bit) presenter.exclude16BitService(mode, service) - else if (service is Service128Bit) presenter.exclude128BitService(mode, service) - ll_data.removeView(serviceItem) + else if (service is Service128Bit) presenter.exclude128BitService( + mode, + service + ) + llData.removeView(serviceItem.root) } - ll_data.addView(serviceItem) + llData.addView(serviceItem.root) } } } - layout.addView(baseContainer) + layout.addView(baseContainer.root) } @SuppressLint("MissingPermission") - private fun addCompleteLocalNameData(layout: ViewGroup, baseContainer: View, mode: DataMode) { - val itemContainer = prepareItemContainer(baseContainer, BluetoothAdapter.getDefaultAdapter().name) - itemContainer.ib_remove.visibility = View.GONE + private fun addCompleteLocalNameData( + layout: ViewGroup, baseContainer: DataTypeLayoutBinding, + mode: DataMode + ) { + val itemContainer = + prepareItemContainer(baseContainer, BluetoothAdapter.getDefaultAdapter().name) + itemContainer.ibRemove.visibility = View.GONE setDataSpinnerItemState(false, mode, 0) presenter.includeCompleteLocalName(mode) - baseContainer.ib_remove.setOnClickListener { - layout.removeView(baseContainer) + baseContainer.ibRemove.setOnClickListener { + layout.removeView(baseContainer.root) presenter.excludeCompleteLocalName(mode) setDataSpinnerItemState(true, mode, 0) } - baseContainer.ll_data.addView(itemContainer) - layout.addView(baseContainer) + baseContainer.llData.addView(itemContainer.root) + layout.addView(baseContainer.root) } - private fun addTxPowerData(layout: ViewGroup, baseContainer: View, mode: DataMode) { - val itemContainer = prepareItemContainer(baseContainer, getString(R.string.unit_value_dbm, getTxPower())) + private fun addTxPowerData( + layout: ViewGroup, baseContainer: DataTypeLayoutBinding, + mode: DataMode + ) { + val itemContainer = + prepareItemContainer(baseContainer, getString(R.string.unit_value_dbm, getTxPower())) - et_tx_power.addTextChangedListener(object : TextWatcher { + binding.advConfigParam.etTxPower.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - if (Validator.isTxPowerValid(et_tx_power.text.toString())) { - itemContainer.tv_data_text.text = getString(R.string.unit_value_dbm, et_tx_power.text.toString().toInt()) + if (Validator.isTxPowerValid(binding.advConfigParam.etTxPower.text.toString())) { + itemContainer.tvDataText.text = + getString( + R.string.unit_value_dbm, + binding.advConfigParam.etTxPower.text.toString().toInt() + ) } } }) - itemContainer.ib_remove.visibility = View.GONE + itemContainer.ibRemove.visibility = View.GONE setDataSpinnerItemState(false, mode, 2) presenter.includeTxPower(mode) - baseContainer.ib_remove.setOnClickListener { - layout.removeView(baseContainer) + baseContainer.ibRemove.setOnClickListener { + layout.removeView(baseContainer.root) presenter.excludeTxPower(mode) setDataSpinnerItemState(true, mode, 2) } - baseContainer.ll_data.addView(itemContainer) - layout.addView(baseContainer) + baseContainer.llData.addView(itemContainer.root) + layout.addView(baseContainer.root) } - private fun addManufacturerSpecificData(layout: ViewGroup, baseContainer: View, mode: DataMode, extra: Any? = null) { + private fun addManufacturerSpecificData( + layout: ViewGroup, + baseContainer: DataTypeLayoutBinding, + mode: DataMode, + extra: Any? = null + ) { if (extra == null) { - ManufacturerDataDialog(presenter.getManufacturers(mode), object : ManufacturerDataDialog.Callback { - override fun onSave(manufacturer: Manufacturer) { - val itemContainer = prepareItemContainer(baseContainer, manufacturer.getAsDescriptiveText()) - itemContainer.ib_remove.visibility = View.GONE - - presenter.includeManufacturerSpecificData(mode, manufacturer) + ManufacturerDataDialog( + presenter.getManufacturers(mode), + object : ManufacturerDataDialog.Callback { + override fun onSave(manufacturer: Manufacturer) { + val itemContainer = + prepareItemContainer(baseContainer, manufacturer.getAsDescriptiveText()) + itemContainer.ibRemove.visibility = View.GONE + + presenter.includeManufacturerSpecificData(mode, manufacturer) + + baseContainer.ibRemove.setOnClickListener { + presenter.excludeManufacturerSpecificData(mode, manufacturer) + layout.removeView(baseContainer.root) + } - baseContainer.ib_remove.setOnClickListener { - presenter.excludeManufacturerSpecificData(mode, manufacturer) - layout.removeView(baseContainer) + baseContainer.llData.addView(itemContainer.root) + layout.addView(baseContainer.root) } - - baseContainer.ll_data.addView(itemContainer) - layout.addView(baseContainer) - } - }).show(supportFragmentManager, "dialog_manufacturer_data") + }).show(supportFragmentManager, "dialog_manufacturer_data") } else { val manufacturer = extra as Manufacturer - val itemContainer = prepareItemContainer(baseContainer, manufacturer.getAsDescriptiveText()) - itemContainer.ib_remove.visibility = View.GONE + val itemContainer = + prepareItemContainer(baseContainer, manufacturer.getAsDescriptiveText()) + itemContainer.ibRemove.visibility = View.GONE - baseContainer.ib_remove.setOnClickListener { + baseContainer.ibRemove.setOnClickListener { presenter.excludeManufacturerSpecificData(mode, manufacturer) - layout.removeView(baseContainer) + layout.removeView(baseContainer.root) } - baseContainer.ll_data.addView(itemContainer) - layout.addView(baseContainer) + baseContainer.llData.addView(itemContainer.root) + layout.addView(baseContainer.root) } } - private fun prepareBaseContainer(layout: ViewGroup, type: DataType): View { - val container = LayoutInflater.from(this@AdvertiserConfigActivity).inflate(R.layout.data_type_layout, layout, false) - setBaseContainerTitle(container, type) - return container + private fun prepareBaseContainer(layout: ViewGroup, type: DataType): + DataTypeLayoutBinding { + lateinit var binding: DataTypeLayoutBinding + val container = LayoutInflater.from(this@AdvertiserConfigActivity) + binding = DataTypeLayoutBinding.inflate(container) + setBaseContainerTitle(binding, type) + return binding } - private fun setBaseContainerTitle(container: View, type: DataType) { - val name = "".plus(type.getIdentifier().plus(" ").plus(translator.getDataTypeAsString(type))) - container.tv_name.text = Html.fromHtml(name, Html.FROM_HTML_MODE_LEGACY) + private fun prepareItemContainer( + baseContainer: DataTypeLayoutBinding, + text: String + ): DataTypeItemBinding { + lateinit var binding: DataTypeItemBinding + val container = LayoutInflater.from(this@AdvertiserConfigActivity) + binding = DataTypeItemBinding.inflate(container) + binding.tvDataText.text = text + return binding } - private fun prepareItemContainer(baseContainer: View, text: String): View { - val container = LayoutInflater.from(this@AdvertiserConfigActivity).inflate(R.layout.data_type_item, baseContainer.ll_data, false) - container.tv_data_text.text = text - return container + private fun setBaseContainerTitle(container: DataTypeLayoutBinding, type: DataType) { + val name = "".plus( + type.getIdentifier().plus(" ").plus(translator.getDataTypeAsString(type)) + ) + container.tvName.text = Html.fromHtml(name, Html.FROM_HTML_MODE_LEGACY) } - private fun removeServicesIfAllowed(layout: ViewGroup, container: View, count: Int, mode: DataMode, type: DataType) { + private fun removeServicesIfAllowed( + layout: ViewGroup, + container: DataTypeLayoutBinding, + count: Int, + mode: DataMode, + type: DataType + ) { if (count > 0 && AdvertiserStorage(this).shouldDisplayRemoveServicesDialog()) { RemoveServicesDialog(object : RemoveServicesDialog.Callback { override fun onOkClicked() { presenter.excludeServices(mode, type) - layout.removeView(container) - setDataSpinnerItemState(true, mode, if (type == DataType.COMPLETE_16_BIT) 3 else 4) + layout.removeView(container.root) + setDataSpinnerItemState( + true, + mode, + if (type == DataType.COMPLETE_16_BIT) 3 else 4 + ) } }).show(supportFragmentManager, "dialog_remove_services") } else { presenter.excludeServices(mode, type) - layout.removeView(container) + layout.removeView(container.root) setDataSpinnerItemState(true, mode, if (type == DataType.COMPLETE_16_BIT) 3 else 4) } } private fun setDataSpinnerItemState(enabled: Boolean, mode: DataMode, position: Int) { - if (mode == DataMode.ADVERTISING_DATA) (sp_advertising_data.adapter as DataTypeAdapter).setItemState(enabled, position) - else (sp_scan_response_data.adapter as DataTypeAdapter).setItemState(enabled, position) - } - override fun updateAvailableBytes(advDataBytes: Int, scanRespDataBytes: Int, maxPacketSize: Int, includeFlags: Boolean) { - changeDataContainerPadding(advDataBytes < maxPacketSize, ll_data_advertising_data) - changeDataContainerPadding(scanRespDataBytes < maxPacketSize, ll_data_scan_resp_data) - - ll_data_advertising_data.findViewById(R.id.data_flags).visibility = if (includeFlags) View.VISIBLE else View.GONE + if (mode == DataMode.ADVERTISING_DATA) (binding.advConfigData.spAdvertisingData.adapter as DataTypeAdapter) + .setItemState( + enabled, + position + ) + else (binding.advConfigData.spScanResponseData.adapter as DataTypeAdapter).setItemState( + enabled, + position + ) + } - tv_adv_data_available_bytes.setTextAppearance(if (advDataBytes >= 0) R.style.TextViewNoteInfo else R.style.TextViewNoteWarning) - tv_adv_data_available_bytes.text = (if (advDataBytes >= 0) getString(R.string.advertiser_note_x_bytes_available, advDataBytes) + override fun updateAvailableBytes( + advDataBytes: Int, + scanRespDataBytes: Int, + maxPacketSize: Int, + includeFlags: Boolean + ) { + changeDataContainerPadding( + advDataBytes < maxPacketSize, + binding.advConfigData.llDataAdvertisingData.root + ) + changeDataContainerPadding( + scanRespDataBytes < maxPacketSize, + binding.advConfigData.llDataScanRespData.root + ) + + binding.advConfigData.llDataAdvertisingData.dataFlags.visibility = + if (includeFlags) View.VISIBLE else View.GONE + + binding.advConfigData.tvAdvDataAvailableBytes.setTextAppearance(if (advDataBytes >= 0) R.style.TextViewNoteInfo else R.style.TextViewNoteWarning) + binding.advConfigData.tvAdvDataAvailableBytes.text = (if (advDataBytes >= 0) getString( + R.string.advertiser_note_x_bytes_available, + advDataBytes + ) else getString(R.string.advertiser_note_x_bytes_beyond, -advDataBytes)) - tv_scan_resp_available_bytes.setTextAppearance(if (scanRespDataBytes >= 0) R.style.TextViewNoteInfo else R.style.TextViewNoteWarning) - tv_scan_resp_available_bytes.text = (if (scanRespDataBytes >= 0) getString(R.string.advertiser_note_x_bytes_available, scanRespDataBytes) - else getString(R.string.advertiser_note_x_bytes_beyond, -scanRespDataBytes)) + binding.advConfigData.tvScanRespAvailableBytes.setTextAppearance(if (scanRespDataBytes >= 0) R.style.TextViewNoteInfo else R.style.TextViewNoteWarning) + binding.advConfigData.tvScanRespAvailableBytes.text = + (if (scanRespDataBytes >= 0) getString( + R.string.advertiser_note_x_bytes_available, + scanRespDataBytes + ) + else getString(R.string.advertiser_note_x_bytes_beyond, -scanRespDataBytes)) } private fun changeDataContainerPadding(showPadding: Boolean, container: View) { - val padding8Dp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics).toInt() + val padding8Dp = + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics) + .toInt() if (showPadding) container.setPadding(0, padding8Dp, 0, padding8Dp) else container.setPadding(0, 0, 0, 0) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/adapters/AdvertiserAdapter.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/adapters/AdvertiserAdapter.kt index ddbb72d0..875f3fde 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/adapters/AdvertiserAdapter.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/adapters/AdvertiserAdapter.kt @@ -112,9 +112,13 @@ class AdvertiserAdapter( } private fun handleDetailsView(item: Advertiser) { + println("Show Advertiser Data ${item.data}") + viewBinding.llAdvertisementDetails.apply { removeAllViews() - addView(AdvertiserDetails(itemView.context).getAdvertiserDetailsView(item, translator)) + + val advertiserDetailContainer = AdvertiserDetails(itemView.context).getAdvertiserDetailsView(item, translator) + addView(advertiserDetailContainer.rootView,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) } toggleDetailsView(item.displayDetailsView) } @@ -131,6 +135,7 @@ class AdvertiserAdapter( } fun toggleDetailsView(displayDetails: Boolean) { + println("displayDetails:--> $displayDetails") viewBinding.llAdvertisementDetails.visibility = if (displayDetails) View.VISIBLE else View.GONE viewBinding.expandArrow.setState(displayDetails) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/adapters/DataTypeAdapter.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/adapters/DataTypeAdapter.kt index e049539a..c60f5d12 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/adapters/DataTypeAdapter.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/adapters/DataTypeAdapter.kt @@ -6,13 +6,18 @@ import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.LinearLayout -import com.siliconlabs.bledemo.features.configure.advertiser.models.DataTypeItem import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.spinner_data_type_item_layout.view.* +import com.siliconlabs.bledemo.databinding.SpinnerDataTypeItemLayoutBinding +import com.siliconlabs.bledemo.features.configure.advertiser.models.DataTypeItem -class DataTypeAdapter(context: Context, - private val values: List, - private val callback: Callback) : ArrayAdapter(context, R.layout.spinner_data_type_item_layout, values) { +//import kotlinx.android.synthetic.main.spinner_data_type_item_layout.view.* + +class DataTypeAdapter( + context: Context, + private val values: List, + private val callback: Callback +) : ArrayAdapter(context, R.layout.spinner_data_type_item_layout, values) { + private lateinit var binding: SpinnerDataTypeItemLayoutBinding override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { return createViewFromResource(position, parent) @@ -22,15 +27,25 @@ class DataTypeAdapter(context: Context, return createViewFromResource(position, parent) } + private fun createViewFromResource(position: Int, parent: ViewGroup?): View { - return (LayoutInflater.from(context).inflate(R.layout.spinner_data_type_item_layout, parent, false) as LinearLayout).apply { - tv_identifier.text = values[position].identifier - tv_name.text = values[position].name - tv_identifier.isEnabled = isEnabled(position) - tv_name.isEnabled = isEnabled(position) + binding = SpinnerDataTypeItemLayoutBinding.inflate(LayoutInflater.from(context)).apply { + tvIdentifier.text = values[position].identifier + tvName.text = values[position].name + tvIdentifier.isEnabled = isEnabled(position) + tvName.isEnabled = isEnabled(position) + + tvName.setOnClickListener { + callback.onItemClick(position) + } + tvIdentifier.setOnClickListener { + callback.onItemClick(position) + } + //TODO + //if (isEnabled(position)) setOnClickListener { callback.onItemClick(position) } - if (isEnabled(position)) setOnClickListener { callback.onItemClick(position) } } + return binding.root } override fun isEnabled(position: Int): Boolean { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/LeaveAdvertiserConfigDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/LeaveAdvertiserConfigDialog.kt index 6ee5b7df..315ec04b 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/LeaveAdvertiserConfigDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/LeaveAdvertiserConfigDialog.kt @@ -7,30 +7,35 @@ import android.view.ViewGroup import com.siliconlabs.bledemo.features.configure.advertiser.utils.AdvertiserStorage import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.dialog_info_ok_cancel.* +import com.siliconlabs.bledemo.databinding.DialogInfoOkCancelBinding + +//mport kotlinx.android.synthetic.main.dialog_info_ok_cancel.* class LeaveAdvertiserConfigDialog(var callback: Callback) : BaseDialogFragment() { + private lateinit var binding:DialogInfoOkCancelBinding override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_info_ok_cancel, container, false) + // return inflater.inflate(R.layout.dialog_info_ok_cancel, container, false) + binding = DialogInfoOkCancelBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - tv_dialog_title.text = context?.getString(R.string.title_unsaved_changes) - tv_dialog_content.text = context?.getString(R.string.advertiser_note_leave_advertiser_config) + binding.tvDialogTitle.text = context?.getString(R.string.title_unsaved_changes) + binding.tvDialogContent.text = context?.getString(R.string.advertiser_note_leave_advertiser_config) - btn_ok.text = context?.getString(R.string.button_yes) - btn_cancel.text = context?.getString(R.string.button_no) + binding.btnOk.text = context?.getString(R.string.button_yes) + binding.btnCancel.text = context?.getString(R.string.button_no) - btn_ok.setOnClickListener { - if (cb_dont_show_again.isChecked) AdvertiserStorage(requireContext()).setShouldDisplayLeaveAdvertiserConfigDialog(false) + binding.btnOk.setOnClickListener { + if (binding.cbDontShowAgain.isChecked) AdvertiserStorage(requireContext()).setShouldDisplayLeaveAdvertiserConfigDialog(false) callback.onYesClicked() dismiss() } - btn_cancel.setOnClickListener { + binding.btnCancel.setOnClickListener { dismiss() callback.onNoClicked() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/ManufacturerDataDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/ManufacturerDataDialog.kt index 91338a94..d38b64b4 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/ManufacturerDataDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/ManufacturerDataDialog.kt @@ -15,32 +15,48 @@ import com.siliconlabs.bledemo.features.configure.advertiser.models.Manufacturer import com.siliconlabs.bledemo.features.configure.advertiser.utils.Validator import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.DialogDataManufacturerBinding import com.siliconlabs.bledemo.utils.Converters -import kotlinx.android.synthetic.main.dialog_data_manufacturer.view.* -class ManufacturerDataDialog(private val manufacturers: List, val callback: Callback) : BaseDialogFragment() { +//import kotlinx.android.synthetic.main.dialog_data_manufacturer.view.* - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_data_manufacturer, container, false).apply { +class ManufacturerDataDialog( + private val manufacturers: List, + val callback: Callback +) : BaseDialogFragment() { - btn_clear.setOnClickListener { - et_company_identifier.setText("") - et_data_in_hex_format.setText("") + private lateinit var binding: DialogDataManufacturerBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogDataManufacturerBinding.inflate(inflater, container, false).apply { + + btnClear.setOnClickListener { + etCompanyIdentifier.setText("") + etDataInHexFormat.setText("") } - btn_cancel.setOnClickListener { + btnCancel.setOnClickListener { dismiss() } - btn_save.setOnClickListener { - handleSave(et_company_identifier, et_data_in_hex_format) + btnSave.setOnClickListener { + handleSave(etCompanyIdentifier, etDataInHexFormat) dismiss() } - verifyDataCorrectness(et_company_identifier, et_data_in_hex_format, btn_save, tv_id_already_exists) - handleClickOnCompanyIdentifiers(btn_company_identifiers) - + verifyDataCorrectness( + etCompanyIdentifier, + etDataInHexFormat, + btnSave, + tvIdAlreadyExists + ) + handleClickOnCompanyIdentifiers(btnCompanyIdentifiers) } + return binding.root } private fun identifierCurrentlyExists(currentId: String): Boolean { @@ -53,15 +69,23 @@ class ManufacturerDataDialog(private val manufacturers: List, val } } - private fun verifyDataCorrectness(etIdentifier: EditText, etData: EditText, btnSave: Button, tvNote: TextView) { + private fun verifyDataCorrectness( + etIdentifier: EditText, + etData: EditText, + btnSave: Button, + tvNote: TextView + ) { etIdentifier.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { val id = etIdentifier.text.toString() val data = etData.text.toString() - tvNote.visibility = if(identifierCurrentlyExists(id)) View.VISIBLE else View.GONE - btnSave.isEnabled = (Validator.isCompanyIdentifierValid(id) && Validator.isCompanyDataValid(data) && !identifierCurrentlyExists(id)) + tvNote.visibility = if (identifierCurrentlyExists(id)) View.VISIBLE else View.GONE + btnSave.isEnabled = + (Validator.isCompanyIdentifierValid(id) && Validator.isCompanyDataValid(data) && !identifierCurrentlyExists( + id + )) } }) @@ -71,15 +95,19 @@ class ManufacturerDataDialog(private val manufacturers: List, val override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { val id = etIdentifier.text.toString() val data = etData.text.toString() - tvNote.visibility = if(identifierCurrentlyExists(id)) View.VISIBLE else View.GONE - btnSave.isEnabled = (Validator.isCompanyIdentifierValid(id) && Validator.isCompanyDataValid(data) && !identifierCurrentlyExists(id)) + tvNote.visibility = if (identifierCurrentlyExists(id)) View.VISIBLE else View.GONE + btnSave.isEnabled = + (Validator.isCompanyIdentifierValid(id) && Validator.isCompanyDataValid(data) && !identifierCurrentlyExists( + id + )) } }) } private fun handleClickOnCompanyIdentifiers(btn: Button) { btn.setOnClickListener { - val uriUrl = Uri.parse("https://" + getString(R.string.advertiser_url_company_identifiers)) + val uriUrl = + Uri.parse("https://" + getString(R.string.advertiser_url_company_identifiers)) val launchBrowser = Intent(Intent.ACTION_VIEW, uriUrl) startActivity(launchBrowser) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/RemoveAdvertiserDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/RemoveAdvertiserDialog.kt index 00257b96..7c9c360a 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/RemoveAdvertiserDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/RemoveAdvertiserDialog.kt @@ -7,23 +7,34 @@ import android.view.ViewGroup import com.siliconlabs.bledemo.features.configure.advertiser.utils.AdvertiserStorage import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.dialog_info_ok_cancel.view.* +import com.siliconlabs.bledemo.databinding.DialogInfoOkCancelBinding -class RemoveAdvertiserDialog(val callback: Callback) : BaseDialogFragment() { +//import kotlinx.android.synthetic.main.dialog_info_ok_cancel.view.* - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_info_ok_cancel, container, false).apply { +class RemoveAdvertiserDialog(val callback: Callback) : BaseDialogFragment() { + private lateinit var binding: DialogInfoOkCancelBinding + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogInfoOkCancelBinding.inflate(inflater, container, false).apply { - tv_dialog_title.text = context.getString(R.string.advertiser_title_remove_advertiser) - tv_dialog_content.text = context.getString(R.string.advertiser_note_remove_advertiser) + tvDialogTitle.text = + requireContext().getString(R.string.advertiser_title_remove_advertiser) + tvDialogContent.text = + requireContext().getString(R.string.advertiser_note_remove_advertiser) - btn_ok.setOnClickListener { - if (cb_dont_show_again.isChecked) AdvertiserStorage(context).setShouldDisplayRemoveAdvertiserDialog(false) + btnOk.setOnClickListener { + if (binding.cbDontShowAgain.isChecked) AdvertiserStorage(requireContext()).setShouldDisplayRemoveAdvertiserDialog( + false + ) callback.onOkClicked() dismiss() } - btn_cancel.setOnClickListener { dismiss() } + btnCancel.setOnClickListener { dismiss() } } + return binding.root } interface Callback { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/RemoveServicesDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/RemoveServicesDialog.kt index fdca76c1..f7e44485 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/RemoveServicesDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/RemoveServicesDialog.kt @@ -7,23 +7,35 @@ import android.view.ViewGroup import com.siliconlabs.bledemo.features.configure.advertiser.utils.AdvertiserStorage import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.dialog_info_ok_cancel.view.* +import com.siliconlabs.bledemo.databinding.DialogInfoOkCancelBinding + +//import kotlinx.android.synthetic.main.dialog_info_ok_cancel.view.* class RemoveServicesDialog(val callback: Callback) : BaseDialogFragment() { + private lateinit var binding: DialogInfoOkCancelBinding - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_info_ok_cancel, container, false).apply { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogInfoOkCancelBinding.inflate(inflater, container, false).apply { - tv_dialog_title.text = context.getString(R.string.advertiser_title_remove_service_list) - tv_dialog_content.text = context.getString(R.string.advertiser_note_remove_services) + tvDialogTitle.text = + requireContext().getString(R.string.advertiser_title_remove_service_list) + tvDialogContent.text = + requireContext().getString(R.string.advertiser_note_remove_services) - btn_ok.setOnClickListener { - if (cb_dont_show_again.isChecked) AdvertiserStorage(context).setShouldDisplayRemoveServicesDialog(false) + btnOk.setOnClickListener { + if (cbDontShowAgain.isChecked) AdvertiserStorage(requireContext()).setShouldDisplayRemoveServicesDialog( + false + ) callback.onOkClicked() dismiss() } - btn_cancel.setOnClickListener { dismiss() } + btnCancel.setOnClickListener { dismiss() } } + return binding.root } interface Callback { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/Service128BitDataDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/Service128BitDataDialog.kt index 1f4b1fc1..dfa62f55 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/Service128BitDataDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/Service128BitDataDialog.kt @@ -12,22 +12,29 @@ import com.siliconlabs.bledemo.features.configure.advertiser.models.Service128Bi import com.siliconlabs.bledemo.features.configure.advertiser.utils.Validator import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.dialog_data_128bit_service.view.* +import com.siliconlabs.bledemo.databinding.DialogData128bitServiceBinding +//import kotlinx.android.synthetic.main.dialog_data_128bit_service.view.* import java.util.* class Service128BitDataDialog(val callback: Callback) : BaseDialogFragment() { - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_data_128bit_service, container, false).apply { - btn_cancel.setOnClickListener { dismiss() } - btn_save.setOnClickListener { - handleSave(et_128bit_service) + private lateinit var binding: DialogData128bitServiceBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogData128bitServiceBinding.inflate(inflater, container, false).apply { + btnCancel.setOnClickListener { dismiss() } + btnSave.setOnClickListener { + handleSave(et128bitService) dismiss() } - btn_clear.setOnClickListener { et_128bit_service.setText("") } + btnClear.setOnClickListener { et128bitService.setText("") } - handleUuidChanges(et_128bit_service, btn_save) + handleUuidChanges(et128bitService, btnSave) } + return binding.root } private fun handleSave(et: EditText) { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/Service16BitDataDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/Service16BitDataDialog.kt index 4e95f82d..ecbd2644 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/Service16BitDataDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/dialogs/Service16BitDataDialog.kt @@ -20,27 +20,37 @@ import com.siliconlabs.bledemo.features.configure.advertiser.utils.Translator import com.siliconlabs.bledemo.features.configure.advertiser.utils.Validator import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment -import kotlinx.android.synthetic.main.dialog_data_16bit_service.view.* -import kotlinx.android.synthetic.main.dialog_data_manufacturer.view.btn_cancel -import kotlinx.android.synthetic.main.dialog_data_manufacturer.view.btn_save +import com.siliconlabs.bledemo.databinding.DialogData16bitServiceBinding +//import kotlinx.android.synthetic.main.dialog_data_16bit_service.view.* +//import kotlinx.android.synthetic.main.dialog_data_manufacturer.view.btn_cancel +//import kotlinx.android.synthetic.main.dialog_data_manufacturer.view.btn_save class Service16BitDataDialog(val callback: Callback) : BaseDialogFragment() { private lateinit var predefinedServices: List - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_data_16bit_service, container, false).apply { - - predefinedServices = Translator(context).get16BitServices() - val adapter = Service16BitAdapter(context, Translator(context).get16BitServices()) - actv_16bit_services.setAdapter(adapter) - - btn_cancel.setOnClickListener { dismiss() } - btn_save.setOnClickListener { handleSave(actv_16bit_services) } - btn_clear.setOnClickListener { actv_16bit_services.setText("") } - - verifyDataCorrectness(actv_16bit_services, btn_save) - handleClickOnBluetoothGattServices(btn_bluetooth_gatt_services) + private lateinit var binding: DialogData16bitServiceBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogData16bitServiceBinding.inflate(inflater, container, false).apply { + + predefinedServices = Translator(requireContext()).get16BitServices() + val adapter = Service16BitAdapter( + requireContext(), + Translator(requireContext()).get16BitServices() + ) + actv16bitServices.setAdapter(adapter) + + btnCancel.setOnClickListener { dismiss() } + btnSave.setOnClickListener { handleSave(actv16bitServices) } + btnClear.setOnClickListener { actv16bitServices.setText("") } + + verifyDataCorrectness(actv16bitServices, btnSave) + handleClickOnBluetoothGattServices(btnBluetoothGattServices) } + return binding.root } private fun handleSave(actv: AutoCompleteTextView) { @@ -64,14 +74,18 @@ class Service16BitDataDialog(val callback: Callback) : BaseDialogFragment() { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { val text = actv.text.toString() - saveBtn.isEnabled = Validator.isCompanyIdentifierValid(text) || isPredefinedService(text, predefinedServices) + saveBtn.isEnabled = Validator.isCompanyIdentifierValid(text) || isPredefinedService( + text, + predefinedServices + ) } }) } private fun handleClickOnBluetoothGattServices(button: Button) { button.setOnClickListener { - val uriUrl = Uri.parse("https://" + getString(R.string.advertiser_url_bluetooth_gatt_services)) + val uriUrl = + Uri.parse("https://" + getString(R.string.advertiser_url_bluetooth_gatt_services)) val launchBrowser = Intent(Intent.ACTION_VIEW, uriUrl) startActivity(launchBrowser) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/fragments/AdvertiserFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/fragments/AdvertiserFragment.kt index 1c79d45c..83a510a4 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/fragments/AdvertiserFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/fragments/AdvertiserFragment.kt @@ -25,6 +25,7 @@ import com.siliconlabs.bledemo.home_screen.activities.MainActivity import com.siliconlabs.bledemo.common.other.CardViewListDecoration import com.siliconlabs.bledemo.home_screen.base.BaseServiceDependentMainMenuFragment import com.siliconlabs.bledemo.home_screen.base.BluetoothDependent +import com.siliconlabs.bledemo.home_screen.base.NotificationDependent class AdvertiserFragment : BaseServiceDependentMainMenuFragment(), AdvertiserAdapter.OnItemClickListener { @@ -50,8 +51,9 @@ class AdvertiserFragment : BaseServiceDependentMainMenuFragment(), AdvertiserAda viewModelStore, AdvertiserViewModel.Factory(AdvertiserStorage(requireContext())) ).get(AdvertiserViewModel::class.java) - service = (activity as MainActivity).bluetoothService!! - + if(null != (activity as MainActivity).bluetoothService){ + service = (activity as MainActivity).bluetoothService!! + } initMainViewValues() setUiListeners() observeChanges() @@ -114,6 +116,8 @@ class AdvertiserFragment : BaseServiceDependentMainMenuFragment(), AdvertiserAda } } + + private fun initAdapter() { advertiserAdapter = AdvertiserAdapter(viewModel?.advertisers?.value ?: arrayListOf(), this) viewBinding.fragmentMainView.rvMainView.apply { @@ -185,6 +189,7 @@ class AdvertiserFragment : BaseServiceDependentMainMenuFragment(), AdvertiserAda } else { fullScreenInfo.root.visibility = View.VISIBLE rvMainView.visibility = View.GONE + restoreHiddenUI() } } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/viewmodels/AdvertiserViewModel.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/viewmodels/AdvertiserViewModel.kt index 6f3e9984..e8580cf2 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/viewmodels/AdvertiserViewModel.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/viewmodels/AdvertiserViewModel.kt @@ -116,7 +116,9 @@ class AdvertiserViewModel(private val advertiserStorage: AdvertiserStorage) : Vi fun switchAllItemsOff() { _advertisers.value?.forEachIndexed { index, advertiser -> - if (advertiser.isRunning) { stopAdvertiserItem(index) } + //if (advertiser.isRunning) { + stopAdvertiserItem(index) + //} } saveAdvertiserList() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/views/AdvertiserDetails.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/views/AdvertiserDetails.kt index c0d3904b..7a5e03ec 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/views/AdvertiserDetails.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/advertiser/views/AdvertiserDetails.kt @@ -23,45 +23,46 @@ class AdvertiserDetails(val context: Context) { val advertisingType = (if (data.isLegacy) context.getString(R.string.advertiser_label_legacy) else context.getString(R.string.advertiser_label_extended)).plus(" (").plus(translator.getString(data.mode)).plus(")") - addView(DetailsRow(context, context.getString(R.string.advertiser_title_advertising_type), advertisingType)) - if (!data.isLegacy) addView(DetailsRow(context, context.getString(R.string.Bluetooth_5_Advertising_Extension), getAdvertisingExtensionText(context, translator, item.data))) + addView(DetailsRow(context, context.getString(R.string.advertiser_title_advertising_type), advertisingType).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) + + if (!data.isLegacy) addView(DetailsRow(context, context.getString(R.string.Bluetooth_5_Advertising_Extension), getAdvertisingExtensionText(context, translator, item.data)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) if (data.isAdvertisingData()) { - if (data.mode.isConnectable()) addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_flags), context.getString(R.string.advertiser_label_flags_default))) + if (data.mode.isConnectable()) addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_flags), context.getString(R.string.advertiser_label_flags_default)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) if (data.advertisingData.includeCompleteLocalName) - addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_local_name), getCompleteLocalName(context, data.advertisingData))) + addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_local_name), getCompleteLocalName(context, data.advertisingData)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) for (manufacturer in data.advertisingData.manufacturers) - addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_manufacturer_specific_data), getManufacturerData(manufacturer))) + addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_manufacturer_specific_data), getManufacturerData(manufacturer)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) if (data.advertisingData.includeTxPower) - addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_tx_power), getTxPower(context, data))) + addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_tx_power), getTxPower(context, data)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) if (data.advertisingData.services16Bit.size > 0) - addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_16bit_service), get16BitServicesText(item.data.advertisingData))) + addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_16bit_service), get16BitServicesText(item.data.advertisingData)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) if (data.advertisingData.services128Bit.size > 0) - addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_128bit_service), get128BitServicesText(item.data.advertisingData))) + addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_128bit_service), get128BitServicesText(item.data.advertisingData)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) } if (data.isScanRespData()) { if (data.scanResponseData.includeCompleteLocalName) - addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_local_name), getCompleteLocalName(context, data.scanResponseData))) + addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_local_name), getCompleteLocalName(context, data.scanResponseData)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) for (manufacturer in data.scanResponseData.manufacturers) - addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_manufacturer_specific_data), getManufacturerData(manufacturer))) + addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_manufacturer_specific_data), getManufacturerData(manufacturer)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) if (data.scanResponseData.includeTxPower) - addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_tx_power), getTxPower(context, data))) + addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_tx_power), getTxPower(context, data)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) if (data.scanResponseData.services16Bit.size > 0) - addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_16bit_service), get16BitServicesText(item.data.scanResponseData))) + addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_16bit_service), get16BitServicesText(item.data.scanResponseData)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) if (data.scanResponseData.services128Bit.size > 0) - addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_128bit_service), get128BitServicesText(item.data.scanResponseData))) + addView(DetailsRow(context, context.getString(R.string.advertiser_data_type_complete_128bit_service), get128BitServicesText(item.data.scanResponseData)).binding.root,LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) } } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/activities/GattServerActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/activities/GattServerActivity.kt index e6c07df1..8f3a2441 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/activities/GattServerActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/activities/GattServerActivity.kt @@ -25,17 +25,21 @@ import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.remove import com.siliconlabs.bledemo.features.configure.gatt_configurator.viewmodels.GattServerViewModel import com.siliconlabs.bledemo.features.configure.gatt_configurator.viewmodels.GattServerViewModel.Validation import com.siliconlabs.bledemo.common.other.EqualVerticalItemDecoration -import kotlinx.android.synthetic.main.activity_gatt_server.* +import com.siliconlabs.bledemo.databinding.ActivityGattServerBinding + +//import kotlinx.android.synthetic.main.activity_gatt_server.* class GattServerActivity : BaseActivity(), ServiceListener, AddServiceListener { private lateinit var viewModel: GattServerViewModel private lateinit var adapter: EditGattServerAdapter private var savedServerState: GattServer? = null + private lateinit var binding: ActivityGattServerBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_gatt_server) + binding = ActivityGattServerBinding.inflate(layoutInflater) + setContentView(binding.root) viewModel = ViewModelProvider(this).get(GattServerViewModel::class.java) initViewModel() @@ -63,7 +67,12 @@ class GattServerActivity : BaseActivity(), ServiceListener, AddServiceListener { viewModel.validation.observe(this, Observer { when (it) { - Validation.INVALID_NAME -> Toast.makeText(this, R.string.gatt_configurator_toast_invalid_gatt_server_name, Toast.LENGTH_SHORT).show() + Validation.INVALID_NAME -> Toast.makeText( + this, + R.string.gatt_configurator_toast_invalid_gatt_server_name, + Toast.LENGTH_SHORT + ).show() + else -> saveGattServer() } }) @@ -79,7 +88,7 @@ class GattServerActivity : BaseActivity(), ServiceListener, AddServiceListener { } private fun handleGattServerNameChanges() { - et_gatt_server_name.addTextChangedListener(object : TextWatcher { + binding.etGattServerName.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { @@ -91,7 +100,7 @@ class GattServerActivity : BaseActivity(), ServiceListener, AddServiceListener { private fun prepopulateFields() { viewModel.getGattServerName()?.let { name -> - et_gatt_server_name.setText(name) + binding.etGattServerName.setText(name) } } @@ -105,20 +114,21 @@ class GattServerActivity : BaseActivity(), ServiceListener, AddServiceListener { private fun exitServerConfigView() { if (hasConfigurationChanged() && - GattConfiguratorStorage(this).shouldDisplayLeaveGattServerConfigDialog()) { + GattConfiguratorStorage(this).shouldDisplayLeaveGattServerConfigDialog() + ) { LeaveGattServerConfigDialog(object : LeaveGattServerConfigDialog.Callback { override fun onYesClicked() { - viewModel.validateGattServer(et_gatt_server_name.text.toString()) + viewModel.validateGattServer(binding.etGattServerName.text.toString()) } + override fun onNoClicked() { super@GattServerActivity.onBackPressed() } }).show(supportFragmentManager, "dialog_leave_gatt_server_config") - } - else super@GattServerActivity.onBackPressed() + } else super@GattServerActivity.onBackPressed() } - private fun hasConfigurationChanged() : Boolean { + private fun hasConfigurationChanged(): Boolean { return savedServerState != viewModel.getGattServer() } @@ -130,19 +140,21 @@ class GattServerActivity : BaseActivity(), ServiceListener, AddServiceListener { override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.save_gatt_server -> { - viewModel.validateGattServer(et_gatt_server_name.text.toString()) + viewModel.validateGattServer(binding.etGattServerName.text.toString()) true } + android.R.id.home -> { exitServerConfigView() true } + else -> super.onOptionsItemSelected(item) } } private fun saveGattServer() { - viewModel.setGattServerName(et_gatt_server_name.text.toString()) + viewModel.setGattServerName(binding.etGattServerName.text.toString()) val intent = Intent().apply { putExtra(EXTRA_GATT_SERVER_POSITION, viewModel.getPosition()!!) putExtra(EXTRA_GATT_SERVER, viewModel.getGattServer()) @@ -154,10 +166,12 @@ class GattServerActivity : BaseActivity(), ServiceListener, AddServiceListener { private fun initAdapter() { adapter = EditGattServerAdapter(viewModel.getServiceList()!!, this, this) - rv_edit_gatt_server.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) - rv_edit_gatt_server.addItemDecoration( - EqualVerticalItemDecoration(resources.getDimensionPixelSize(R.dimen.edit_gatt_server_adapter_margin))) - rv_edit_gatt_server.adapter = adapter + binding.rvEditGattServer.layoutManager = + LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + binding.rvEditGattServer.addItemDecoration( + EqualVerticalItemDecoration(resources.getDimensionPixelSize(R.dimen.edit_gatt_server_adapter_margin)) + ) + binding.rvEditGattServer.adapter = adapter } override fun onCopyService(service: Service) { @@ -179,6 +193,7 @@ class GattServerActivity : BaseActivity(), ServiceListener, AddServiceListener { } override fun onBackPressed() { + super.onBackPressed() exitServerConfigView() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/CharacteristicDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/CharacteristicDialog.kt index 8658cf2c..88b86003 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/CharacteristicDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/CharacteristicDialog.kt @@ -12,25 +12,35 @@ import android.widget.AutoCompleteTextView import android.widget.CheckBox import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment +import com.siliconlabs.bledemo.databinding.DialogGattServerCharacteristicBinding import com.siliconlabs.bledemo.features.configure.gatt_configurator.adapters.Characteristic16BitAdapter import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.* import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.GattUtils import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.Validator -import kotlinx.android.synthetic.main.dialog_gatt_server_characteristic.* -import kotlinx.android.synthetic.main.gatt_configurator_initial_value.* -import kotlinx.android.synthetic.main.dialog_add_characteristic_properties_content.* -class CharacteristicDialog(val listener: CharacteristicChangeListener, val characteristic: Characteristic = Characteristic()) : BaseDialogFragment() { +//import kotlinx.android.synthetic.main.dialog_gatt_server_characteristic.* +//import kotlinx.android.synthetic.main.gatt_configurator_initial_value.* +//import kotlinx.android.synthetic.main.dialog_add_characteristic_properties_content.* - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_gatt_server_characteristic, container, false) +class CharacteristicDialog( + val listener: CharacteristicChangeListener, + val characteristic: Characteristic = Characteristic() +) : BaseDialogFragment() { + private lateinit var binding: DialogGattServerCharacteristicBinding + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogGattServerCharacteristicBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - initACTV(actv_characteristic_name, SearchMode.BY_NAME) - initACTV(actv_characteristic_uuid, SearchMode.BY_UUID) + initACTV(binding.actvCharacteristicName, SearchMode.BY_NAME) + initACTV(binding.actvCharacteristicUuid, SearchMode.BY_UUID) initInitialValueSpinner() handleClickEvents() @@ -42,60 +52,68 @@ class CharacteristicDialog(val listener: CharacteristicChangeListener, val chara } private fun handleClickEvents() { - btn_save.setOnClickListener { + binding.btnSave.setOnClickListener { setCharacteristicState() listener.onCharacteristicChanged(characteristic) dismiss() } - btn_cancel.setOnClickListener { + binding.btnCancel.setOnClickListener { dismiss() } - btn_clear.setOnClickListener { + binding.btnClear.setOnClickListener { clearAllFields() } } private fun prepopulateFields() { - actv_characteristic_name.setText(characteristic.name) - actv_characteristic_uuid.setText(characteristic.uuid?.uuid) + binding.actvCharacteristicName.setText(characteristic.name) + binding.actvCharacteristicUuid.setText(characteristic.uuid?.uuid) prepopulateProperties() prepopulatePropertyTypes() prepopulateInitialValue() } private fun prepopulateProperties() { + // binding.propertiesContent.apply { characteristic.properties.apply { - sw_read.isChecked = containsKey(Property.READ) - sw_write.isChecked = containsKey(Property.WRITE) - sw_write_without_resp.isChecked = containsKey(Property.WRITE_WITHOUT_RESPONSE) - sw_reliable_write.isChecked = containsKey(Property.RELIABLE_WRITE) - sw_notify.isChecked = containsKey(Property.NOTIFY) - sw_indicate.isChecked = containsKey(Property.INDICATE) + binding.propertiesContent.swRead.isChecked = containsKey(Property.READ) + binding.propertiesContent.swWrite.isChecked = containsKey(Property.WRITE) + binding.propertiesContent.swWriteWithoutResp.isChecked = + containsKey(Property.WRITE_WITHOUT_RESPONSE) + binding.propertiesContent.swReliableWrite.isChecked = + containsKey(Property.RELIABLE_WRITE) + binding.propertiesContent.swNotify.isChecked = containsKey(Property.NOTIFY) + binding.propertiesContent.swIndicate.isChecked = containsKey(Property.INDICATE) } + } private fun prepopulatePropertyTypes() { characteristic.properties.apply { this[Property.READ]?.apply { - cb_read_bonded.isChecked = contains(Property.Type.BONDED) - cb_read_mitm.isChecked = contains(Property.Type.AUTHENTICATED) + binding.propertiesContent.cbReadBonded.isChecked = contains(Property.Type.BONDED) + binding.propertiesContent.cbReadMitm.isChecked = + contains(Property.Type.AUTHENTICATED) } this[Property.WRITE]?.apply { - cb_write_bonded.isChecked = contains(Property.Type.BONDED) - cb_write_mitm.isChecked = contains(Property.Type.AUTHENTICATED) + binding.propertiesContent.cbWriteBonded.isChecked = contains(Property.Type.BONDED) + binding.propertiesContent.cbWriteMitm.isChecked = + contains(Property.Type.AUTHENTICATED) return } this[Property.WRITE_WITHOUT_RESPONSE]?.apply { - cb_write_bonded.isChecked = contains(Property.Type.BONDED) - cb_write_mitm.isChecked = contains(Property.Type.AUTHENTICATED) + binding.propertiesContent.cbWriteBonded.isChecked = contains(Property.Type.BONDED) + binding.propertiesContent.cbWriteMitm.isChecked = + contains(Property.Type.AUTHENTICATED) return } this[Property.RELIABLE_WRITE]?.apply { - cb_write_bonded.isChecked = contains(Property.Type.BONDED) - cb_write_mitm.isChecked = contains(Property.Type.AUTHENTICATED) + binding.propertiesContent.cbWriteBonded.isChecked = contains(Property.Type.BONDED) + binding.propertiesContent.cbWriteMitm.isChecked = + contains(Property.Type.AUTHENTICATED) return } } @@ -104,184 +122,221 @@ class CharacteristicDialog(val listener: CharacteristicChangeListener, val chara private fun prepopulateInitialValue() { when (characteristic.value?.type) { Value.Type.USER -> { - sp_initial_value.setSelection(POSITION_INITIAL_VALUE_EMPTY) + binding.initialValue.spInitialValue.setSelection(POSITION_INITIAL_VALUE_EMPTY) } + Value.Type.UTF_8 -> { - sp_initial_value.setSelection(POSITION_INITIAL_VALUE_TEXT) - et_initial_value_text.setText(characteristic.value?.value) + binding.initialValue.spInitialValue.setSelection(POSITION_INITIAL_VALUE_TEXT) + binding.initialValue.etInitialValueText.setText(characteristic.value?.value) } + Value.Type.HEX -> { - sp_initial_value.setSelection(POSITION_INITIAL_VALUE_HEX) - et_initial_value_hex.setText(characteristic.value?.value) + binding.initialValue.spInitialValue.setSelection(POSITION_INITIAL_VALUE_HEX) + binding.initialValue.etInitialValueHex.setText(characteristic.value?.value) } + else -> Unit } } private fun setCharacteristicState() { - characteristic.name = actv_characteristic_name.text.toString() - characteristic.uuid = Uuid(actv_characteristic_uuid.text.toString()) + characteristic.name = binding.actvCharacteristicName.text.toString() + characteristic.uuid = Uuid(binding.actvCharacteristicUuid.text.toString()) setPropertiesState() setInitialValue() } private fun setPropertiesState() { characteristic.properties.clear() - if (sw_read.isChecked) characteristic.properties[Property.READ] = getSelectedReadTypes() - if (sw_write.isChecked) characteristic.properties[Property.WRITE] = getSelectedWriteTypes() - if (sw_write_without_resp.isChecked) characteristic.properties[Property.WRITE_WITHOUT_RESPONSE] = getSelectedWriteTypes() - if (sw_reliable_write.isChecked) characteristic.properties[Property.RELIABLE_WRITE] = getSelectedWriteTypes() - if (sw_notify.isChecked) characteristic.properties[Property.NOTIFY] = hashSetOf() - if (sw_indicate.isChecked) characteristic.properties[Property.INDICATE] = hashSetOf() + if (binding.propertiesContent.swRead.isChecked) characteristic.properties[Property.READ] = + getSelectedReadTypes() + if (binding.propertiesContent.swWrite.isChecked) characteristic.properties[Property.WRITE] = + getSelectedWriteTypes() + if (binding.propertiesContent.swWriteWithoutResp.isChecked) characteristic.properties[Property.WRITE_WITHOUT_RESPONSE] = + getSelectedWriteTypes() + if (binding.propertiesContent.swReliableWrite.isChecked) characteristic.properties[Property.RELIABLE_WRITE] = + getSelectedWriteTypes() + if (binding.propertiesContent.swNotify.isChecked) characteristic.properties[Property.NOTIFY] = + hashSetOf() + if (binding.propertiesContent.swIndicate.isChecked) characteristic.properties[Property.INDICATE] = + hashSetOf() handlePropertiesUsingDescriptors() } private fun handlePropertiesUsingDescriptors() { - if (sw_reliable_write.isChecked) setReliableWritePropertyDescriptor() + if (binding.propertiesContent.swReliableWrite.isChecked) setReliableWritePropertyDescriptor() else removeReliableWritePropertyDescriptor() - if (sw_indicate.isChecked || sw_notify.isChecked) setIndicateOrNotifyPropertyDescriptor() + if (binding.propertiesContent.swIndicate.isChecked || binding.propertiesContent.swNotify.isChecked) setIndicateOrNotifyPropertyDescriptor() else removeIndicateOrNotifyPropertyDescriptor() } private fun setReliableWritePropertyDescriptor() { - val result = characteristic.descriptors.filter { it.name == GattUtils.getReliableWriteDescriptor().name && it.isPredefined } + val result = + characteristic.descriptors.filter { it.name == GattUtils.getReliableWriteDescriptor().name && it.isPredefined } if (result.isEmpty()) { characteristic.descriptors.add(GattUtils.getReliableWriteDescriptor()) } } private fun removeReliableWritePropertyDescriptor() { - val descriptor = characteristic.descriptors.find { it.name == GattUtils.getReliableWriteDescriptor().name && it.isPredefined } + val descriptor = + characteristic.descriptors.find { it.name == GattUtils.getReliableWriteDescriptor().name && it.isPredefined } characteristic.descriptors.remove(descriptor) } private fun setIndicateOrNotifyPropertyDescriptor() { - val result = characteristic.descriptors.filter { it.name == GattUtils.getIndicateOrNotifyDescriptor().name && it.isPredefined } + val result = + characteristic.descriptors.filter { it.name == GattUtils.getIndicateOrNotifyDescriptor().name && it.isPredefined } if (result.isEmpty()) { characteristic.descriptors.add(GattUtils.getIndicateOrNotifyDescriptor()) } } private fun removeIndicateOrNotifyPropertyDescriptor() { - val descriptor = characteristic.descriptors.find { it.name == GattUtils.getIndicateOrNotifyDescriptor().name && it.isPredefined } + val descriptor = + characteristic.descriptors.find { it.name == GattUtils.getIndicateOrNotifyDescriptor().name && it.isPredefined } characteristic.descriptors.remove(descriptor) } private fun getSelectedReadTypes(): HashSet { return hashSetOf().apply { - if (cb_read_bonded.isChecked) add(Property.Type.BONDED) - if (cb_read_mitm.isChecked) add(Property.Type.AUTHENTICATED) + if (binding.propertiesContent.cbReadBonded.isChecked) add(Property.Type.BONDED) + if (binding.propertiesContent.cbReadMitm.isChecked) add(Property.Type.AUTHENTICATED) } } private fun getSelectedWriteTypes(): HashSet { return hashSetOf().apply { - if (cb_write_bonded.isChecked) add(Property.Type.BONDED) - if (cb_write_mitm.isChecked) add(Property.Type.AUTHENTICATED) + if (binding.propertiesContent.cbWriteBonded.isChecked) add(Property.Type.BONDED) + if (binding.propertiesContent.cbWriteMitm.isChecked) add(Property.Type.AUTHENTICATED) } } private fun setInitialValue() { - when (sp_initial_value.selectedItemPosition) { + when (binding.initialValue.spInitialValue.selectedItemPosition) { POSITION_INITIAL_VALUE_EMPTY -> { characteristic.value = Value( - value = "", - type = Value.Type.USER + value = "", + type = Value.Type.USER ) } + POSITION_INITIAL_VALUE_TEXT -> { characteristic.value = Value( - value = et_initial_value_text.text.toString(), - type = Value.Type.UTF_8, - length = et_initial_value_text.text.length + value = binding.initialValue.etInitialValueText.text.toString(), + type = Value.Type.UTF_8, + length = binding.initialValue.etInitialValueText.text.length ) } + POSITION_INITIAL_VALUE_HEX -> { characteristic.value = Value( - value = et_initial_value_hex.text.toString(), - type = Value.Type.HEX, - length = et_initial_value_hex.length() / 2 + value = binding.initialValue.etInitialValueHex.text.toString(), + type = Value.Type.HEX, + length = binding.initialValue.etInitialValueHex.length() / 2 ) } } } private fun handleNameChanges() { - actv_characteristic_name.addTextChangedListener(object : TextWatcher { + binding.actvCharacteristicName.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - btn_save.isEnabled = isInputValid() + binding.btnSave.isEnabled = isInputValid() } }) } private fun handleUuidChanges() { - actv_characteristic_uuid.addTextChangedListener(object : TextWatcher { + binding.actvCharacteristicUuid.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { val len = s?.length - if ((len == 8 || len == 13 || len == 18 || len == 23) && count > before) actv_characteristic_uuid.append("-") - btn_save.isEnabled = isInputValid() + if ((len == 8 || len == 13 || len == 18 || len == 23) && count > before) binding.actvCharacteristicUuid.append( + "-" + ) + binding.btnSave.isEnabled = isInputValid() } }) } private fun handleInitialValueEditTextChanges() { - et_initial_value_text.addTextChangedListener(object : TextWatcher { + binding.initialValue.etInitialValueText.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - btn_save.isEnabled = isInputValid() + binding.btnSave.isEnabled = isInputValid() } }) - et_initial_value_hex.addTextChangedListener(object : TextWatcher { + binding.initialValue.etInitialValueHex.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - btn_save.isEnabled = isInputValid() + binding.btnSave.isEnabled = isInputValid() } }) } private fun isAnyPropertyChecked(): Boolean { - return sw_read.isChecked || - sw_write.isChecked || - sw_write_without_resp.isChecked || - sw_reliable_write.isChecked || - sw_notify.isChecked || - sw_indicate.isChecked + return binding.propertiesContent.swRead.isChecked || + binding.propertiesContent.swWrite.isChecked || + binding.propertiesContent.swWriteWithoutResp.isChecked || + binding.propertiesContent.swReliableWrite.isChecked || + binding.propertiesContent.swNotify.isChecked || + binding.propertiesContent.swIndicate.isChecked } private fun handlePropertyStateChanges() { - sw_read.setOnCheckedChangeListener { _, _ -> - btn_save.isEnabled = isInputValid() - setPropertyParametersState(sw_read.isChecked, cb_read_bonded, cb_read_mitm) + binding.propertiesContent.swRead.setOnCheckedChangeListener { _, _ -> + binding.btnSave.isEnabled = isInputValid() + setPropertyParametersState( + binding.propertiesContent.swRead.isChecked, + binding.propertiesContent.cbReadBonded, + binding.propertiesContent.cbReadMitm + ) } - sw_write.setOnCheckedChangeListener { _, _ -> - btn_save.isEnabled = isInputValid() - setPropertyParametersState(sw_write.isChecked || sw_write_without_resp.isChecked || sw_reliable_write.isChecked, cb_write_bonded, cb_write_mitm) + binding.propertiesContent.swWrite.setOnCheckedChangeListener { _, _ -> + binding.btnSave.isEnabled = isInputValid() + setPropertyParametersState( + binding.propertiesContent.swWrite.isChecked || binding.propertiesContent.swWriteWithoutResp.isChecked || binding.propertiesContent.swReliableWrite.isChecked, + binding.propertiesContent.cbWriteBonded, + binding.propertiesContent.cbWriteMitm + ) } - sw_write_without_resp.setOnCheckedChangeListener { _, _ -> - btn_save.isEnabled = isInputValid() - setPropertyParametersState(sw_write.isChecked || sw_write_without_resp.isChecked || sw_reliable_write.isChecked, cb_write_bonded, cb_write_mitm) + binding.propertiesContent.swWriteWithoutResp.setOnCheckedChangeListener { _, _ -> + binding.btnSave.isEnabled = isInputValid() + setPropertyParametersState( + binding.propertiesContent.swWrite.isChecked || binding.propertiesContent.swWriteWithoutResp.isChecked || binding.propertiesContent.swReliableWrite.isChecked, + binding.propertiesContent.cbWriteBonded, + binding.propertiesContent.cbWriteMitm + ) } - sw_reliable_write.setOnCheckedChangeListener { _, _ -> - btn_save.isEnabled = isInputValid() - setPropertyParametersState(sw_write.isChecked || sw_write_without_resp.isChecked || sw_reliable_write.isChecked, cb_write_bonded, cb_write_mitm) + binding.propertiesContent.swReliableWrite.setOnCheckedChangeListener { _, _ -> + binding.btnSave.isEnabled = isInputValid() + setPropertyParametersState( + binding.propertiesContent.swWrite.isChecked || binding.propertiesContent.swWriteWithoutResp.isChecked || binding.propertiesContent.swReliableWrite.isChecked, + binding.propertiesContent.cbWriteBonded, + binding.propertiesContent.cbWriteMitm + ) } - sw_notify.setOnCheckedChangeListener { _, _ -> - btn_save.isEnabled = isInputValid() + binding.propertiesContent.swNotify.setOnCheckedChangeListener { _, _ -> + binding.btnSave.isEnabled = isInputValid() } - sw_indicate.setOnCheckedChangeListener { _, _ -> - btn_save.isEnabled = isInputValid() + binding.propertiesContent.swIndicate.setOnCheckedChangeListener { _, _ -> + binding.btnSave.isEnabled = isInputValid() } } - private fun setPropertyParametersState(switchState: Boolean, cbBonded: CheckBox, cbMitm: CheckBox) { + private fun setPropertyParametersState( + switchState: Boolean, + cbBonded: CheckBox, + cbMitm: CheckBox + ) { cbBonded.isEnabled = switchState cbMitm.isEnabled = switchState if (!switchState) { @@ -292,21 +347,23 @@ class CharacteristicDialog(val listener: CharacteristicChangeListener, val chara private fun isInputValid(): Boolean { return isAnyPropertyChecked() - && actv_characteristic_name.text.toString().isNotEmpty() - && isUuidValid(actv_characteristic_uuid.text.toString()) + && binding.actvCharacteristicName.text.toString().isNotEmpty() + && isUuidValid(binding.actvCharacteristicUuid.text.toString()) && isInitialValueValid() } private fun isInitialValueValid(): Boolean { - when (sp_initial_value.selectedItemPosition) { + when (binding.initialValue.spInitialValue.selectedItemPosition) { POSITION_INITIAL_VALUE_EMPTY -> { return true } + POSITION_INITIAL_VALUE_TEXT -> { - return et_initial_value_text.text.toString().isNotEmpty() + return binding.initialValue.etInitialValueText.text.toString().isNotEmpty() } + POSITION_INITIAL_VALUE_HEX -> { - return Validator.isHexValid(et_initial_value_hex.text.toString()) + return Validator.isHexValid(binding.initialValue.etInitialValueHex.text.toString()) } } return true @@ -317,55 +374,71 @@ class CharacteristicDialog(val listener: CharacteristicChangeListener, val chara } private fun initACTV(actv: AutoCompleteTextView, searchMode: SearchMode) { - val adapter = Characteristic16BitAdapter(requireContext(), GattUtils.get16BitCharacteristics(), searchMode) + val adapter = Characteristic16BitAdapter( + requireContext(), + GattUtils.get16BitCharacteristics(), + searchMode + ) actv.setAdapter(adapter) actv.setOnItemClickListener { _, _, position, _ -> val characteristic = adapter.getItem(position) - actv_characteristic_name.setText(characteristic?.name) - actv_characteristic_uuid.setText(characteristic?.getIdentifierAsString()) + binding.actvCharacteristicName.setText(characteristic?.name) + binding.actvCharacteristicUuid.setText(characteristic?.getIdentifierAsString()) actv.setSelection(actv.length()) hideKeyboard() } } private fun initInitialValueSpinner() { - val adapter = ArrayAdapter(requireContext(), R.layout.spinner_item_layout, resources.getStringArray(R.array.gatt_configurator_initial_value)) + val adapter = ArrayAdapter( + requireContext(), + R.layout.spinner_item_layout, + resources.getStringArray(R.array.gatt_configurator_initial_value) + ) adapter.setDropDownViewResource(R.layout.spinner_dropdown_item_layout) - sp_initial_value.adapter = adapter + binding.initialValue.spInitialValue.adapter = adapter handleInitialValueSelection() } private fun handleInitialValueSelection() { - sp_initial_value.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - btn_save.isEnabled = isInputValid() - et_initial_value_text.visibility = if (position == POSITION_INITIAL_VALUE_TEXT) View.VISIBLE else View.GONE - ll_initial_value_hex.visibility = if (position == POSITION_INITIAL_VALUE_HEX) View.VISIBLE else View.GONE + binding.initialValue.spInitialValue.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + binding.btnSave.isEnabled = isInputValid() + binding.initialValue.etInitialValueText.visibility = + if (position == POSITION_INITIAL_VALUE_TEXT) View.VISIBLE else View.GONE + binding.initialValue.llInitialValueHex.visibility = + if (position == POSITION_INITIAL_VALUE_HEX) View.VISIBLE else View.GONE + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + } } - - override fun onNothingSelected(parent: AdapterView<*>?) { - } - } } private fun clearAllFields() { - actv_characteristic_name.setText("") - actv_characteristic_uuid.setText("") - sw_read.isChecked = true - sw_write.isChecked = false - sw_write_without_resp.isChecked = false - sw_reliable_write.isChecked = false - sw_notify.isChecked = false - sw_indicate.isChecked = false - cb_read_bonded.isChecked = false - cb_read_mitm.isChecked = false - cb_write_bonded.isChecked = false - cb_write_mitm.isChecked = false - sp_initial_value.setSelection(0) - et_initial_value_text.setText("") - et_initial_value_hex.setText("") + binding.actvCharacteristicName.setText("") + binding.actvCharacteristicUuid.setText("") + binding.propertiesContent.swRead.isChecked = true + binding.propertiesContent.swWrite.isChecked = false + binding.propertiesContent.swWriteWithoutResp.isChecked = false + binding.propertiesContent.swReliableWrite.isChecked = false + binding.propertiesContent.swNotify.isChecked = false + binding.propertiesContent.swIndicate.isChecked = false + binding.propertiesContent.cbReadBonded.isChecked = false + binding.propertiesContent.cbReadMitm.isChecked = false + binding.propertiesContent.cbWriteBonded.isChecked = false + binding.propertiesContent.cbWriteMitm.isChecked = false + binding.initialValue.spInitialValue.setSelection(0) + binding.initialValue.etInitialValueText.setText("") + binding.initialValue.etInitialValueHex.setText("") } interface CharacteristicChangeListener { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/DescriptorDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/DescriptorDialog.kt index 99829dde..9a19cb5a 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/DescriptorDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/DescriptorDialog.kt @@ -13,25 +13,36 @@ import android.widget.CheckBox import androidx.appcompat.widget.SwitchCompat import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment +import com.siliconlabs.bledemo.databinding.DialogGattServerDescriptorBinding import com.siliconlabs.bledemo.features.configure.gatt_configurator.adapters.Descriptor16BitAdapter import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.* import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.GattUtils import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.Validator -import kotlinx.android.synthetic.main.dialog_gatt_server_descriptor.* -import kotlinx.android.synthetic.main.dialog_add_descriptor_properties_content.* -import kotlinx.android.synthetic.main.gatt_configurator_initial_value.* -class DescriptorDialog(val listener: DescriptorChangeListener, val descriptor: Descriptor = Descriptor()) : BaseDialogFragment() { - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_gatt_server_descriptor, container, false) +//import kotlinx.android.synthetic.main.dialog_gatt_server_descriptor.* +//import kotlinx.android.synthetic.main.dialog_add_descriptor_properties_content.* +//import kotlinx.android.synthetic.main.gatt_configurator_initial_value.* + +class DescriptorDialog( + val listener: DescriptorChangeListener, + val descriptor: Descriptor = Descriptor() +) : BaseDialogFragment() { + private lateinit var binding: DialogGattServerDescriptorBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogGattServerDescriptorBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - initACTV(actv_descriptor_name, SearchMode.BY_NAME) - initACTV(actv_descriptor_uuid, SearchMode.BY_UUID) + initACTV(binding.actvDescriptorName, SearchMode.BY_NAME) + initACTV(binding.actvDescriptorUuid, SearchMode.BY_UUID) initInitialValueSpinner() handleClickEvents() @@ -43,24 +54,24 @@ class DescriptorDialog(val listener: DescriptorChangeListener, val descriptor: D } private fun handleClickEvents() { - btn_save.setOnClickListener { + binding.btnSave.setOnClickListener { setDescriptorState() listener.onDescriptorChanged(descriptor) dismiss() } - btn_clear.setOnClickListener { + binding.btnClear.setOnClickListener { clearAllFields() } - btn_cancel.setOnClickListener { + binding.btnCancel.setOnClickListener { dismiss() } } private fun prepopulateFields() { - actv_descriptor_name.setText(descriptor.name) - actv_descriptor_uuid.setText(descriptor.uuid?.uuid) + binding.actvDescriptorName.setText(descriptor.name) + binding.actvDescriptorUuid.setText(descriptor.uuid?.uuid) prepopulateProperties() prepopulatePropertyTypes() prepopulateInitialValue() @@ -68,21 +79,24 @@ class DescriptorDialog(val listener: DescriptorChangeListener, val descriptor: D private fun prepopulateProperties() { descriptor.properties.apply { - sw_read.isChecked = containsKey(Property.READ) - sw_write.isChecked = containsKey(Property.WRITE) + + binding.propertiesContent.swRead.isChecked = containsKey(Property.READ) + binding.propertiesContent.swWrite.isChecked = containsKey(Property.WRITE) } } private fun prepopulatePropertyTypes() { descriptor.properties.apply { this[Property.READ]?.apply { - cb_read_bonded.isChecked = contains(Property.Type.BONDED) - cb_read_mitm.isChecked = contains(Property.Type.AUTHENTICATED) + + binding.propertiesContent.cbReadBonded.isChecked = contains(Property.Type.BONDED) + binding.propertiesContent.cbReadMitm.isChecked = contains(Property.Type.AUTHENTICATED) } this[Property.WRITE]?.apply { - cb_write_bonded.isChecked = contains(Property.Type.BONDED) - cb_write_mitm.isChecked = contains(Property.Type.AUTHENTICATED) + + binding.propertiesContent.cbWriteBonded.isChecked = contains(Property.Type.BONDED) + binding.propertiesContent.cbWriteMitm.isChecked = contains(Property.Type.AUTHENTICATED) } } } @@ -90,62 +104,81 @@ class DescriptorDialog(val listener: DescriptorChangeListener, val descriptor: D private fun prepopulateInitialValue() { when (descriptor.value?.type) { Value.Type.USER -> { - sp_initial_value.setSelection(POSITION_INITIAL_VALUE_EMPTY) + + binding.initialValue.spInitialValue.setSelection(POSITION_INITIAL_VALUE_EMPTY) } + Value.Type.UTF_8 -> { - sp_initial_value.setSelection(POSITION_INITIAL_VALUE_TEXT) - et_initial_value_text.setText(descriptor.value?.value) + + binding.initialValue.spInitialValue.setSelection(POSITION_INITIAL_VALUE_TEXT) + binding.initialValue.etInitialValueText.setText(descriptor.value?.value) } + Value.Type.HEX -> { - sp_initial_value.setSelection(POSITION_INITIAL_VALUE_HEX) - et_initial_value_hex.setText(descriptor.value?.value) + + binding.initialValue.spInitialValue.setSelection(POSITION_INITIAL_VALUE_HEX) + binding.initialValue.etInitialValueHex.setText(descriptor.value?.value) } + else -> Unit } } private fun isInitialValueValid(): Boolean { - when (sp_initial_value.selectedItemPosition) { + when (binding.initialValue.spInitialValue.selectedItemPosition) { POSITION_INITIAL_VALUE_EMPTY -> { return true } + POSITION_INITIAL_VALUE_TEXT -> { - return et_initial_value_text.text.toString().isNotEmpty() + return binding.initialValue.etInitialValueText.text.toString().isNotEmpty() } + POSITION_INITIAL_VALUE_HEX -> { - return Validator.isHexValid(et_initial_value_hex.text.toString()) + return Validator.isHexValid(binding.initialValue.etInitialValueHex.text.toString()) } } return true } private fun initACTV(actv: AutoCompleteTextView, searchMode: SearchMode) { - val adapter = Descriptor16BitAdapter(requireContext(), GattUtils.get16BitDescriptors(), searchMode) + val adapter = + Descriptor16BitAdapter(requireContext(), GattUtils.get16BitDescriptors(), searchMode) actv.setAdapter(adapter) actv.setOnItemClickListener { _, _, position, _ -> val descriptor = adapter.getItem(position) - actv_descriptor_name.setText(descriptor?.name) - actv_descriptor_uuid.setText(descriptor?.getIdentifierAsString()) + binding.actvDescriptorName.setText(descriptor?.name) + binding.actvDescriptorUuid.setText(descriptor?.getIdentifierAsString()) actv.setSelection(actv.length()) hideKeyboard() } } private fun initInitialValueSpinner() { - val adapter = ArrayAdapter(requireContext(), R.layout.spinner_item_layout, resources.getStringArray(R.array.gatt_configurator_initial_value)) + val adapter = ArrayAdapter( + requireContext(), + R.layout.spinner_item_layout, + resources.getStringArray(R.array.gatt_configurator_initial_value) + ) adapter.setDropDownViewResource(R.layout.spinner_dropdown_item_layout) - sp_initial_value.adapter = adapter + binding.initialValue.spInitialValue.adapter = adapter handleInitialValueSelection() } private fun handleInitialValueSelection() { - sp_initial_value.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - btn_save.isEnabled = isInputValid() - et_initial_value_text.visibility = if (position == 1) View.VISIBLE else View.GONE - ll_initial_value_hex.visibility = if (position == 2) View.VISIBLE else View.GONE + binding.initialValue.spInitialValue.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + + binding.btnSave.isEnabled = isInputValid() + binding.initialValue.etInitialValueText.visibility = if (position == 1) View.VISIBLE else View.GONE + binding.initialValue.llInitialValueHex.visibility = if (position == 2) View.VISIBLE else View.GONE } override fun onNothingSelected(parent: AdapterView<*>?) { @@ -154,120 +187,133 @@ class DescriptorDialog(val listener: DescriptorChangeListener, val descriptor: D } private fun handleNameChanges() { - actv_descriptor_name.addTextChangedListener(object : TextWatcher { + binding.actvDescriptorName.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - btn_save.isEnabled = isInputValid() + binding.btnSave.isEnabled = isInputValid() } }) } private fun handleUuidChanges() { - actv_descriptor_uuid.addTextChangedListener(object : TextWatcher { + binding.actvDescriptorUuid.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { val len = s?.length - if ((len == 8 || len == 13 || len == 18 || len == 23) && count > before) actv_descriptor_uuid.append("-") - btn_save.isEnabled = isInputValid() + if ((len == 8 || len == 13 || len == 18 || len == 23) && count > before) binding.actvDescriptorUuid.append( + "-" + ) + binding.btnSave.isEnabled = isInputValid() } }) } private fun handleInitialValueEditTextChanges() { - et_initial_value_text.addTextChangedListener(object : TextWatcher { + binding.initialValue.etInitialValueText.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - btn_save.isEnabled = isInputValid() + binding.btnSave.isEnabled = isInputValid() } }) - et_initial_value_hex.addTextChangedListener(object : TextWatcher { + binding.initialValue.etInitialValueHex.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - btn_save.isEnabled = isInputValid() + binding.btnSave.isEnabled = isInputValid() } }) } private fun handlePropertyStateChanges() { - sw_read.setOnCheckedChangeListener { _, _ -> - btn_save.isEnabled = isInputValid() - setPropertyParametersState(sw_read, cb_read_bonded, cb_read_mitm) + binding.propertiesContent.swRead.setOnCheckedChangeListener { _, _ -> + binding.btnSave.isEnabled = isInputValid() + setPropertyParametersState( + binding.propertiesContent.swRead, + binding.propertiesContent.cbReadBonded, + binding.propertiesContent.cbReadMitm + ) } - sw_write.setOnCheckedChangeListener { _, _ -> - btn_save.isEnabled = isInputValid() - setPropertyParametersState(sw_write, cb_write_bonded, cb_write_mitm) + binding.propertiesContent.swWrite.setOnCheckedChangeListener { _, _ -> + binding.btnSave.isEnabled = isInputValid() + setPropertyParametersState(binding.propertiesContent.swWrite, binding.propertiesContent.cbWriteBonded, binding.propertiesContent.cbWriteMitm) } } - private fun setPropertyParametersState(switch: SwitchCompat, cbBonded: CheckBox, cbMitm: CheckBox) { + private fun setPropertyParametersState( + switch: SwitchCompat, + cbBonded: CheckBox, + cbMitm: CheckBox + ) { cbBonded.isEnabled = switch.isChecked cbMitm.isEnabled = switch.isChecked - if(!switch.isChecked) { + if (!switch.isChecked) { cbBonded.isChecked = false cbMitm.isChecked = false } } private fun setDescriptorState() { - descriptor.name = actv_descriptor_name.text.toString() - descriptor.uuid = Uuid(actv_descriptor_uuid.text.toString()) + descriptor.name = binding.actvDescriptorName.text.toString() + descriptor.uuid = Uuid(binding.actvDescriptorUuid.text.toString()) setPropertiesState() setInitialValue() } private fun setPropertiesState() { descriptor.properties.clear() - if (sw_read.isChecked) descriptor.properties[Property.READ] = getSelectedReadTypes() - if (sw_write.isChecked) descriptor.properties[Property.WRITE] = getSelectedWriteTypes() + if (binding.propertiesContent.swRead.isChecked) descriptor.properties[Property.READ] = + getSelectedReadTypes() + if (binding.propertiesContent.swWrite.isChecked) descriptor.properties[Property.WRITE] = getSelectedWriteTypes() } private fun getSelectedReadTypes(): HashSet { return hashSetOf().apply { - if (cb_read_bonded.isChecked) add(Property.Type.BONDED) - if (cb_read_mitm.isChecked) add(Property.Type.AUTHENTICATED) + if ( binding.propertiesContent.cbReadBonded.isChecked) add(Property.Type.BONDED) + if (binding.propertiesContent.cbReadMitm.isChecked) add(Property.Type.AUTHENTICATED) } } private fun getSelectedWriteTypes(): HashSet { return hashSetOf().apply { - if (cb_write_bonded.isChecked) add(Property.Type.BONDED) - if (cb_write_mitm.isChecked) add(Property.Type.AUTHENTICATED) + if (binding.propertiesContent.cbWriteBonded.isChecked) add(Property.Type.BONDED) + if (binding.propertiesContent.cbWriteMitm.isChecked) add(Property.Type.AUTHENTICATED) } } private fun setInitialValue() { - when (sp_initial_value.selectedItemPosition) { + when (binding.initialValue.spInitialValue.selectedItemPosition) { POSITION_INITIAL_VALUE_EMPTY -> { descriptor.value = Value( - value = "", - type = Value.Type.USER + value = "", + type = Value.Type.USER ) } + POSITION_INITIAL_VALUE_TEXT -> { descriptor.value = Value( - value = et_initial_value_text.text.toString(), - type = Value.Type.UTF_8, - length = et_initial_value_text.text.length + value = binding.initialValue.etInitialValueText.text.toString(), + type = Value.Type.UTF_8, + length = binding.initialValue.etInitialValueText.text.length ) } + POSITION_INITIAL_VALUE_HEX -> { descriptor.value = Value( - value = et_initial_value_hex.text.toString(), - type = Value.Type.HEX, - length = et_initial_value_text.length() / 2 + value = binding.initialValue.etInitialValueHex.text.toString(), + type = Value.Type.HEX, + length = binding.initialValue.etInitialValueText.length() / 2 ) } } } private fun isAnyPropertyChecked(): Boolean { - return sw_read.isChecked || - sw_write.isChecked + return binding.propertiesContent.swRead.isChecked || + binding.propertiesContent.swWrite.isChecked } private fun isUuidValid(uuid: String): Boolean { @@ -276,23 +322,23 @@ class DescriptorDialog(val listener: DescriptorChangeListener, val descriptor: D private fun isInputValid(): Boolean { return isAnyPropertyChecked() - && actv_descriptor_name.text.toString().isNotEmpty() - && isUuidValid(actv_descriptor_uuid.text.toString()) + && binding.actvDescriptorName.text.toString().isNotEmpty() + && isUuidValid(binding.actvDescriptorUuid.text.toString()) && isInitialValueValid() } private fun clearAllFields() { - actv_descriptor_name.setText("") - actv_descriptor_uuid.setText("") - sw_read.isChecked = true - sw_write.isChecked = false - cb_read_bonded.isChecked = false - cb_read_mitm.isChecked = false - cb_write_bonded.isChecked = false - cb_write_mitm.isChecked = false - sp_initial_value.setSelection(0) - et_initial_value_text.setText("") - et_initial_value_hex.setText("") + binding.actvDescriptorName.setText("") + binding.actvDescriptorUuid.setText("") + binding.propertiesContent.swRead.isChecked = true + binding.propertiesContent.swWrite.isChecked = false + binding.propertiesContent.cbReadBonded.isChecked = false + binding.propertiesContent.cbReadMitm.isChecked = false + binding.propertiesContent.cbWriteBonded.isChecked = false + binding.propertiesContent.cbWriteMitm.isChecked = false + binding.initialValue.spInitialValue.setSelection(0) + binding.initialValue.etInitialValueText.setText("") + binding.initialValue.etInitialValueHex.setText("") } interface DescriptorChangeListener { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/LeaveGattServerConfigDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/LeaveGattServerConfigDialog.kt index 62754342..63572967 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/LeaveGattServerConfigDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/LeaveGattServerConfigDialog.kt @@ -6,33 +6,41 @@ import android.view.View import android.view.ViewGroup import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment +import com.siliconlabs.bledemo.databinding.DialogInfoOkCancelBinding import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.GattConfiguratorStorage -import kotlinx.android.synthetic.main.dialog_info_ok_cancel.* -class LeaveGattServerConfigDialog(val callback: Callback) : BaseDialogFragment() { +//import kotlinx.android.synthetic.main.dialog_info_ok_cancel.* - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_info_ok_cancel, container, false) +class LeaveGattServerConfigDialog(val callback: Callback) : BaseDialogFragment() { + private lateinit var binding: DialogInfoOkCancelBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogInfoOkCancelBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - tv_dialog_title.text = context?.getString(R.string.title_unsaved_changes) - tv_dialog_content.text = context?.getString(R.string.gatt_configurator_leave_config_dialog_content) + binding.tvDialogTitle.text = context?.getString(R.string.title_unsaved_changes) + binding.tvDialogContent.text = + context?.getString(R.string.gatt_configurator_leave_config_dialog_content) - btn_ok.text = context?.getString(R.string.button_yes) - btn_cancel.text = context?.getString(R.string.button_no) + binding.btnOk.text = context?.getString(R.string.button_yes) + binding.btnCancel.text = context?.getString(R.string.button_no) - btn_ok.setOnClickListener { - if (cb_dont_show_again.isChecked) GattConfiguratorStorage(requireContext()) - .setShouldDisplayLeaveGattServerConfigDialog(false) + binding.btnOk.setOnClickListener { + if (binding.cbDontShowAgain.isChecked + ) GattConfiguratorStorage(requireContext()) + .setShouldDisplayLeaveGattServerConfigDialog(false) callback.onYesClicked() dismiss() } - btn_cancel.setOnClickListener { + binding.btnCancel.setOnClickListener { dismiss() callback.onNoClicked() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/ServiceDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/ServiceDialog.kt index 7f2d888f..5394bdc6 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/ServiceDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/dialogs/ServiceDialog.kt @@ -13,26 +13,36 @@ import android.widget.AutoCompleteTextView import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.bluetooth.parsing.Engine +import com.siliconlabs.bledemo.databinding.DialogGattServerDescriptorBinding +import com.siliconlabs.bledemo.databinding.DialogGattServerServiceBinding import com.siliconlabs.bledemo.utils.UuidUtils import com.siliconlabs.bledemo.features.configure.gatt_configurator.adapters.Service16BitAdapter import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.* import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.GattUtils import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.Validator -import kotlinx.android.synthetic.main.dialog_gatt_server_service.* +//import kotlinx.android.synthetic.main.dialog_gatt_server_service.* import java.util.* -class ServiceDialog(val listener: ServiceChangeListener, var service: Service = Service()) : BaseDialogFragment() { +class ServiceDialog(val listener: ServiceChangeListener, var service: Service = Service()) : + BaseDialogFragment() { private val predefinedServices: List = GattUtils.get16BitServices() + private lateinit var binding: DialogGattServerServiceBinding - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_gatt_server_service, container, false) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogGattServerServiceBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - initACTV(actv_service_name, SearchMode.BY_NAME) - initACTV(actv_service_uuid, SearchMode.BY_UUID) + + initACTV(binding.actvServiceName, SearchMode.BY_NAME) + initACTV(binding.actvServiceUuid, SearchMode.BY_UUID) initServiceTypeSpinner() handleClickEvents() @@ -41,38 +51,43 @@ class ServiceDialog(val listener: ServiceChangeListener, var service: Service = } private fun initACTV(actv: AutoCompleteTextView, searchMode: SearchMode) { - val adapter = Service16BitAdapter(requireContext(), GattUtils.get16BitServices(), searchMode) + val adapter = + Service16BitAdapter(requireContext(), GattUtils.get16BitServices(), searchMode) actv.setAdapter(adapter) actv.setOnItemClickListener { _, _, position, _ -> val service = adapter.getItem(position) - actv_service_name.setText(service?.name) - actv_service_uuid.setText(service?.getIdentifierAsString()) + binding.actvServiceName.setText(service?.name) + binding.actvServiceUuid.setText(service?.getIdentifierAsString()) actv.setSelection(actv.length()) hideKeyboard() } } private fun initServiceTypeSpinner() { - val adapter = ArrayAdapter(requireContext(), R.layout.spinner_item_layout, resources.getStringArray(R.array.gatt_configurator_service_type)) + val adapter = ArrayAdapter( + requireContext(), + R.layout.spinner_item_layout, + resources.getStringArray(R.array.gatt_configurator_service_type) + ) adapter.setDropDownViewResource(R.layout.spinner_dropdown_item_layout) - sp_service_type.adapter = adapter + binding.spServiceType.adapter = adapter } private fun handleClickEvents() { - btn_bluetooth_gatt_services.setOnClickListener { + binding.btnBluetoothGattServices.setOnClickListener { launchBluetoothGattServicesWebPage() } - btn_clear.setOnClickListener { + binding.btnClear.setOnClickListener { resetInput() } - btn_cancel.setOnClickListener { + binding.btnCancel.setOnClickListener { dismiss() } - btn_save.setOnClickListener { + binding.btnSave.setOnClickListener { setServiceState() listener.onServiceChanged(service) dismiss() @@ -80,16 +95,17 @@ class ServiceDialog(val listener: ServiceChangeListener, var service: Service = } private fun setServiceState() { - if (cb_mandatory_requirements.isChecked) handleMandatoryServiceRequirements() + + if (binding.cbMandatoryRequirements.isChecked) handleMandatoryServiceRequirements() service.apply { - name = actv_service_name.text.toString() - uuid = Uuid(actv_service_uuid.text.toString()) + name = binding.actvServiceName.text.toString() + uuid = Uuid(binding.actvServiceUuid.text.toString()) type = getServiceTypeFromSelection() } } private fun handleMandatoryServiceRequirements() { - val uuid = UuidUtils.convert16to128UUID(actv_service_uuid.text.toString()) + val uuid = UuidUtils.convert16to128UUID(binding.actvServiceUuid.text.toString()) service = getMandatoryServiceRequirements(UUID.fromString(uuid)) } @@ -102,7 +118,8 @@ class ServiceDialog(val listener: ServiceChangeListener, var service: Service = val characteristicType = Engine.getCharacteristicByType(characteristicRes.type!!) val characteristic = Characteristic().apply { - name = characteristicType?.name ?: getString(R.string.unknown_characteristic_label) + name = + characteristicType?.name ?: getString(R.string.unknown_characteristic_label) uuid = characteristicType?.let { charType -> Uuid(UuidUtils.convert128to16UUID(charType.uuid.toString())) } @@ -146,18 +163,18 @@ class ServiceDialog(val listener: ServiceChangeListener, var service: Service = } private fun getServiceTypeFromSelection(): Service.Type { - return when (sp_service_type.selectedItemPosition) { + return when (binding.spServiceType.selectedItemPosition) { 0 -> Service.Type.PRIMARY else -> Service.Type.SECONDARY } } private fun handleNameChanges() { - actv_service_name.addTextChangedListener(object : TextWatcher { + binding.actvServiceName.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - btn_save.isEnabled = isInputValid() + binding.btnSave.isEnabled = isInputValid() setMandatoryRequirementsCheckBoxState() setServiceTypeSpinnerState() } @@ -165,14 +182,16 @@ class ServiceDialog(val listener: ServiceChangeListener, var service: Service = } private fun handleUuidChanges() { - actv_service_uuid.addTextChangedListener(object : TextWatcher { + binding.actvServiceUuid.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { val len = s?.length - if ((len == 8 || len == 13 || len == 18 || len == 23) && count > before) actv_service_uuid.append("-") + if ((len == 8 || len == 13 || len == 18 || len == 23) && count > before) binding.actvServiceUuid.append( + "-" + ) - btn_save.isEnabled = isInputValid() + binding.btnSave.isEnabled = isInputValid() setMandatoryRequirementsCheckBoxState() setServiceTypeSpinnerState() } @@ -181,32 +200,34 @@ class ServiceDialog(val listener: ServiceChangeListener, var service: Service = private fun setMandatoryRequirementsCheckBoxState() { if (isInput16BitService()) { - cb_mandatory_requirements.isEnabled = true + binding.cbMandatoryRequirements.isEnabled = true } else { - cb_mandatory_requirements.isEnabled = false - cb_mandatory_requirements.isChecked = false + binding.cbMandatoryRequirements.isEnabled = false + binding.cbMandatoryRequirements.isChecked = false } } private fun setServiceTypeSpinnerState() { if (isInput16BitService()) { - sp_service_type.setSelection(0) - sp_service_type.isEnabled = false + binding.spServiceType.setSelection(0) + binding.spServiceType.isEnabled = false } else { - sp_service_type.isEnabled = true + binding.spServiceType.isEnabled = true } } private fun isInput16BitService(): Boolean { - val name = actv_service_name.text.toString() - val uuid = actv_service_uuid.text.toString() + val name = binding.actvServiceName.text.toString() + val uuid = binding.actvServiceUuid.text.toString() - val result = predefinedServices.filter { it.name == name && it.getIdentifierAsString() == uuid } + val result = + predefinedServices.filter { it.name == name && it.getIdentifierAsString() == uuid } return result.isNotEmpty() } private fun isInputValid(): Boolean { - return actv_service_name.text.toString().isNotEmpty() && isUuidValid(actv_service_uuid.text.toString()) + return binding.actvServiceName.text.toString() + .isNotEmpty() && isUuidValid(binding.actvServiceUuid.text.toString()) } private fun isUuidValid(uuid: String): Boolean { @@ -214,16 +235,17 @@ class ServiceDialog(val listener: ServiceChangeListener, var service: Service = } private fun launchBluetoothGattServicesWebPage() { - val uriUrl = Uri.parse("https://" + getString(R.string.advertiser_url_bluetooth_gatt_services)) + val uriUrl = + Uri.parse("https://" + getString(R.string.advertiser_url_bluetooth_gatt_services)) val launchBrowser = Intent(Intent.ACTION_VIEW, uriUrl) startActivity(launchBrowser) } private fun resetInput() { - actv_service_name.setText("") - actv_service_uuid.setText("") - cb_mandatory_requirements.isChecked = false - sp_service_type.setSelection(0) + binding.actvServiceName.setText("") + binding.actvServiceUuid.setText("") + binding.cbMandatoryRequirements.isChecked = false + binding.spServiceType.setSelection(0) } interface ServiceChangeListener { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/fragments/GattConfiguratorFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/fragments/GattConfiguratorFragment.kt index 1a65818c..0ec0ca88 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/fragments/GattConfiguratorFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/fragments/GattConfiguratorFragment.kt @@ -135,6 +135,7 @@ class GattConfiguratorFragment : BaseMainMenuFragment(), OnClickListener { } else { fullScreenInfo.root.visibility = View.VISIBLE rvMainView.visibility = View.GONE + restoreHiddenUI() } } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/AddServiceViewHolder.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/AddServiceViewHolder.kt index a817eb95..815a7c6f 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/AddServiceViewHolder.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/AddServiceViewHolder.kt @@ -1,15 +1,19 @@ package com.siliconlabs.bledemo.features.configure.gatt_configurator.viewholders import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.AdapterAddServiceBinding import com.siliconlabs.bledemo.features.configure.gatt_configurator.adapters.EditGattServerAdapter.AddServiceListener -import kotlinx.android.synthetic.main.adapter_add_service.view.* -class AddServiceViewHolder(view: View, private val addServiceListener: AddServiceListener) : RecyclerView.ViewHolder(view) { - private val btnAddService = view.btn_add_service +//import kotlinx.android.synthetic.main.adapter_add_service.view.* + +class AddServiceViewHolder( + view: AdapterAddServiceBinding, + private val addServiceListener: AddServiceListener +) : RecyclerView.ViewHolder(view.root) { + private val btnAddService = view.btnAddService + fun bind() { handleClickEvents() @@ -22,9 +26,13 @@ class AddServiceViewHolder(view: View, private val addServiceListener: AddServic } companion object { - fun create(parent: ViewGroup, addServiceListener: AddServiceListener): AddServiceViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_add_service, parent, false) - return AddServiceViewHolder(view, addServiceListener) + fun create( + parent: ViewGroup, + addServiceListener: AddServiceListener + ): AddServiceViewHolder { + val binding = AdapterAddServiceBinding.inflate(LayoutInflater.from(parent.context)) + + return AddServiceViewHolder(binding, addServiceListener) } } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/EditGattServerViewHolder.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/EditGattServerViewHolder.kt index d109bc10..96006058 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/EditGattServerViewHolder.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/EditGattServerViewHolder.kt @@ -1,11 +1,13 @@ package com.siliconlabs.bledemo.features.configure.gatt_configurator.viewholders +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.get import androidx.recyclerview.widget.RecyclerView import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.AdapterEditGattServerBinding import com.siliconlabs.bledemo.features.configure.gatt_configurator.activities.GattServerActivity import com.siliconlabs.bledemo.features.configure.gatt_configurator.adapters.EditGattServerAdapter.ServiceListener import com.siliconlabs.bledemo.features.configure.gatt_configurator.dialogs.CharacteristicDialog @@ -13,18 +15,19 @@ import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.Chara import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.Service import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.removeAsking import com.siliconlabs.bledemo.features.configure.gatt_configurator.views.GattCharacteristicView -import kotlinx.android.synthetic.main.adapter_edit_gatt_server.view.* - -class EditGattServerViewHolder(view: View, val list: ArrayList, val listener: ServiceListener) : RecyclerView.ViewHolder(view) { - private val llCharacteristics = view.ll_characteristics - private val llCharacteristicsOuter = view.ll_characteristics_outer - private val expandArrow = view.expand_arrow - private val tvName = view.tv_service_name - private val tvUuid = view.tv_service_uuid - private val tvType = view.tv_service_type - private val ibCopy = view.ib_copy - private val ibRemove = view.ib_remove - private val btnAddCharacteristic = view.btn_add_characteristic +//import kotlinx.android.synthetic.main.adapter_edit_gatt_server.view.* + +class EditGattServerViewHolder(view: AdapterEditGattServerBinding, val list: ArrayList, val listener: ServiceListener) : + RecyclerView.ViewHolder(view.root) { + private val llCharacteristics = view.llCharacteristics + private val llCharacteristicsOuter = view.llCharacteristicsOuter + private val expandArrow = view.expandArrow + private val tvName = view.tvServiceName + private val tvUuid = view.tvServiceUuid + private val tvType = view.tvServiceType + private val ibCopy = view.ibCopy + private val ibRemove = view.ibRemove + private val btnAddCharacteristic = view.btnAddCharacteristic fun bind(service: Service) { llCharacteristics.removeAllViews() @@ -56,8 +59,8 @@ class EditGattServerViewHolder(view: View, val list: ArrayList, val lis private fun initCharacteristics(characteristics: ArrayList) { for (characteristic in characteristics) { val view = GattCharacteristicView(itemView.context, characteristic) - handleCharacteristicClickEvents(view, characteristics) llCharacteristics.addView(view) + handleCharacteristicClickEvents(view, characteristics) } } @@ -98,11 +101,25 @@ class EditGattServerViewHolder(view: View, val list: ArrayList, val lis llCharacteristics.addView(view) } - private fun editCharacteristic(characteristic: Characteristic, characteristics: ArrayList) { + private fun editCharacteristic( + characteristic: Characteristic, + characteristics: ArrayList + ) { CharacteristicDialog(object : CharacteristicDialog.CharacteristicChangeListener { override fun onCharacteristicChanged(characteristic: Characteristic) { + val index = characteristics.indexOf(characteristic) - (llCharacteristics[index] as GattCharacteristicView).refreshView() + + // Get the view at the specified index in the LinearLayout + val view = llCharacteristics.getChildAt(index) + + if (view is GattCharacteristicView) { + // If the view is a GattCharacteristicView, call refreshView + view.refreshView() + } else { + // Handle the case where the view is not a GattCharacteristicView + Log.e("EditCharacteristic", "View at index $index is not a GattCharacteristicView.") + } } }, characteristic).show((itemView.context as GattServerActivity).supportFragmentManager, "dialog_characteristic") } @@ -123,8 +140,9 @@ class EditGattServerViewHolder(view: View, val list: ArrayList, val lis companion object { fun create(parent: ViewGroup, list: ArrayList, listener: ServiceListener): EditGattServerViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_edit_gatt_server, parent, false) - return EditGattServerViewHolder(view, list, listener) + val binding = AdapterEditGattServerBinding.inflate(LayoutInflater.from(parent.context)) + + return EditGattServerViewHolder(binding, list, listener) } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/GattServerViewHolder.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/GattServerViewHolder.kt index 98bd95de..1e9652e7 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/GattServerViewHolder.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/viewholders/GattServerViewHolder.kt @@ -3,6 +3,7 @@ package com.siliconlabs.bledemo.features.configure.gatt_configurator.viewholders import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.LinearLayout import androidx.recyclerview.widget.RecyclerView import com.google.android.material.button.MaterialButton import com.siliconlabs.bledemo.R @@ -107,7 +108,7 @@ class GattServerViewHolder( if (service.characteristics.size == 1) R.string.gatt_configurator_one_characteristic else R.string.gatt_configurator_n_characteristics, service.characteristics.size ) - )) + ).binding.root, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/views/GattCharacteristicView.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/views/GattCharacteristicView.kt index ab14e305..d84f6e1c 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/views/GattCharacteristicView.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/views/GattCharacteristicView.kt @@ -3,39 +3,45 @@ package com.siliconlabs.bledemo.features.configure.gatt_configurator.views import android.content.Context import android.text.Html import android.util.AttributeSet +import android.util.Log import android.view.LayoutInflater import android.view.View import android.widget.FrameLayout import androidx.core.view.get import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.ViewGattCharacteristicBinding import com.siliconlabs.bledemo.features.configure.gatt_configurator.activities.GattServerActivity import com.siliconlabs.bledemo.features.configure.gatt_configurator.dialogs.DescriptorDialog import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.Characteristic import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.Descriptor import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.Property import com.siliconlabs.bledemo.features.configure.gatt_configurator.utils.removeAsking -import kotlinx.android.synthetic.main.view_gatt_characteristic.view.* + import com.siliconlabs.bledemo.features.configure.gatt_configurator.views.GattDescriptorView.DescriptorListener -class GattCharacteristicView(context: Context, attributeSet: AttributeSet? = null) : FrameLayout(context, attributeSet) { + +class GattCharacteristicView(context: Context, attributeSet: AttributeSet? = null) : + FrameLayout(context, attributeSet) { private var characteristic: Characteristic? = null + lateinit var gattCharacterBinding: ViewGattCharacteristicBinding constructor(context: Context, characteristic: Characteristic) : this(context) { this.characteristic = characteristic + gattCharacterBinding = ViewGattCharacteristicBinding.inflate(LayoutInflater.from(context),this,true) initView(characteristic) handleAddDescriptorClickEvent(characteristic.descriptors) } private fun initView(characteristic: Characteristic) { - tv_characteristic_name.text = characteristic.name + gattCharacterBinding.tvCharacteristicName.text = characteristic.name val uuidHtml = buildString { append("") append(context.getString(R.string.UUID_colon_space)) append("") append(characteristic.uuid?.getAsFormattedText()) } - tv_characteristic_uuid.text = Html.fromHtml(uuidHtml, Html.FROM_HTML_MODE_LEGACY) + gattCharacterBinding.tvCharacteristicUuid.text = Html.fromHtml(uuidHtml, Html.FROM_HTML_MODE_LEGACY) showSelectedProperties(characteristic) showOrHideDescriptorsLabelWithDivider() initDescriptors() @@ -45,24 +51,28 @@ class GattCharacteristicView(context: Context, attributeSet: AttributeSet? = nul characteristic?.let { initView(it) } + gattCharacterBinding.root.invalidate() } private fun hideAllProperties() { - tv_property_read.visibility = View.GONE - tv_property_write.visibility = View.GONE - tv_property_indicate.visibility = View.GONE - tv_property_notify.visibility = View.GONE + + gattCharacterBinding.tvPropertyRead.visibility = View.GONE + gattCharacterBinding.tvPropertyWrite.visibility = View.GONE + gattCharacterBinding.tvPropertyIndicate.visibility = View.GONE + gattCharacterBinding.tvPropertyNotify.visibility = View.GONE } private fun showSelectedProperties(characteristic: Characteristic) { hideAllProperties() characteristic.properties.apply { - if (containsKey(Property.READ)) tv_property_read.visibility = View.VISIBLE - if (containsKey(Property.WRITE)) tv_property_write.visibility = View.VISIBLE - if (containsKey(Property.WRITE_WITHOUT_RESPONSE)) tv_property_write.visibility = View.VISIBLE - if (containsKey(Property.RELIABLE_WRITE)) tv_property_write.visibility = View.VISIBLE - if (containsKey(Property.INDICATE)) tv_property_indicate.visibility = View.VISIBLE - if (containsKey(Property.NOTIFY)) tv_property_notify.visibility = View.VISIBLE + if (containsKey(Property.READ)) gattCharacterBinding.tvPropertyRead.visibility = View.VISIBLE + if (containsKey(Property.WRITE)) gattCharacterBinding.tvPropertyWrite.visibility = View.VISIBLE + if (containsKey(Property.WRITE_WITHOUT_RESPONSE)) gattCharacterBinding.tvPropertyWrite.visibility = + View.VISIBLE + if (containsKey(Property.RELIABLE_WRITE)) gattCharacterBinding.tvPropertyWrite.visibility = + View.VISIBLE + if (containsKey(Property.INDICATE)) gattCharacterBinding.tvPropertyIndicate.visibility = View.VISIBLE + if (containsKey(Property.NOTIFY)) gattCharacterBinding.tvPropertyNotify.visibility = View.VISIBLE } } @@ -71,15 +81,18 @@ class GattCharacteristicView(context: Context, attributeSet: AttributeSet? = nul } private fun initDescriptors() { - ll_descriptors.removeAllViews() + gattCharacterBinding.llDescriptors.removeAllViews() for (descriptor in characteristic?.descriptors!!) { val view = GattDescriptorView(context, descriptor) handleDescriptorClickEvents(view, characteristic?.descriptors!!) - ll_descriptors.addView(view) + gattCharacterBinding.llDescriptors.addView(view) } } - private fun handleDescriptorClickEvents(view: GattDescriptorView, descriptors: ArrayList) { + private fun handleDescriptorClickEvents( + view: GattDescriptorView, + descriptors: ArrayList + ) { view.setDescriptorListener(object : DescriptorListener { override fun onCopyDescriptor(descriptor: Descriptor) { copyDescriptor(descriptor, descriptors) @@ -98,7 +111,8 @@ class GattCharacteristicView(context: Context, attributeSet: AttributeSet? = nul } private fun handleAddDescriptorClickEvent(descriptors: ArrayList) { - btn_add_descriptor.setOnClickListener { + + gattCharacterBinding.btnAddDescriptor.setOnClickListener { DescriptorDialog(object : DescriptorDialog.DescriptorChangeListener { override fun onDescriptorChanged(descriptor: Descriptor) { addDescriptor(descriptor, descriptors) @@ -112,7 +126,7 @@ class GattCharacteristicView(context: Context, attributeSet: AttributeSet? = nul handleDescriptorClickEvents(view, descriptors) descriptors.add(descriptor) - ll_descriptors.addView(view) + gattCharacterBinding.llDescriptors.addView(view) showOrHideDescriptorsLabelWithDivider() } @@ -122,21 +136,32 @@ class GattCharacteristicView(context: Context, attributeSet: AttributeSet? = nul descriptors.add(copiedDescriptor) handleDescriptorClickEvents(view, descriptors) - ll_descriptors.addView(view) + gattCharacterBinding.llDescriptors.addView(view) } - private fun editDescriptor(descriptor: Descriptor, descriptors: ArrayList) { + private fun editDescriptor( + descriptor: Descriptor, + descriptors: ArrayList) { DescriptorDialog(object : DescriptorDialog.DescriptorChangeListener { override fun onDescriptorChanged(descriptor: Descriptor) { val index = descriptors.indexOf(descriptor) - (ll_descriptors[index] as GattDescriptorView).refreshView() + val view = gattCharacterBinding.llDescriptors.getChildAt(index) + if(view is GattDescriptorView){ + view.refreshView() + }else{ + Log.e("EditDescriptor", "View at index $index is not a GattDescriptorView.") + } + } - }, descriptor).show((context as GattServerActivity).supportFragmentManager, "dialog_descriptor") + }, descriptor).show( + (context as GattServerActivity).supportFragmentManager, + "dialog_descriptor" + ) } private fun removeDescriptor(descriptor: Descriptor, descriptors: ArrayList) { val index = descriptors.indexOf(descriptor) - ll_descriptors.removeViewAt(index) + gattCharacterBinding.llDescriptors.removeViewAt(index) descriptors.remove(descriptor) showOrHideDescriptorsLabelWithDivider() @@ -144,23 +169,24 @@ class GattCharacteristicView(context: Context, attributeSet: AttributeSet? = nul private fun showOrHideDescriptorsLabelWithDivider() { characteristic?.descriptors?.apply { - tv_descriptors.visibility = if (isEmpty()) View.GONE else View.VISIBLE - cv_descriptors.visibility = if (isEmpty()) View.GONE else View.VISIBLE + + gattCharacterBinding.tvDescriptors.visibility = if (isEmpty()) View.GONE else View.VISIBLE + gattCharacterBinding.cvDescriptors.visibility = if (isEmpty()) View.GONE else View.VISIBLE } } fun setCharacteristicListener(listener: CharacteristicListener) { - ib_copy.setOnClickListener { + gattCharacterBinding.ibCopy.setOnClickListener { characteristic?.let { listener.onCopyCharacteristic(it) } } - ib_edit.setOnClickListener { + gattCharacterBinding.ibEdit.setOnClickListener { characteristic?.let { listener.onEditCharacteristic(it) } } - ib_remove.setOnClickListener { + gattCharacterBinding.ibRemove.setOnClickListener { characteristic?.let { listener.onRemoveCharacteristic(it) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/views/GattDescriptorView.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/views/GattDescriptorView.kt index 10fd70e5..a9bc5576 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/views/GattDescriptorView.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/configure/gatt_configurator/views/GattDescriptorView.kt @@ -8,33 +8,44 @@ import android.view.LayoutInflater import android.view.View import android.widget.FrameLayout import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.ViewGattDescriptorBinding import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.Descriptor import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.Property -import kotlinx.android.synthetic.main.view_gatt_descriptor.view.* -import kotlinx.android.synthetic.main.view_gatt_descriptor.view.ib_copy -import kotlinx.android.synthetic.main.view_gatt_descriptor.view.ib_edit -import kotlinx.android.synthetic.main.view_gatt_descriptor.view.ib_remove -import kotlinx.android.synthetic.main.view_gatt_descriptor.view.tv_property_read -import kotlinx.android.synthetic.main.view_gatt_descriptor.view.tv_property_write - -class GattDescriptorView(context: Context, attributeSet: AttributeSet? = null) : FrameLayout(context, attributeSet) { + +//import kotlinx.android.synthetic.main.view_gatt_descriptor.view.* +//import kotlinx.android.synthetic.main.view_gatt_descriptor.view.ib_copy +//import kotlinx.android.synthetic.main.view_gatt_descriptor.view.ib_edit +//import kotlinx.android.synthetic.main.view_gatt_descriptor.view.ib_remove +//import kotlinx.android.synthetic.main.view_gatt_descriptor.view.tv_property_read +//import kotlinx.android.synthetic.main.view_gatt_descriptor.view.tv_property_write + +class GattDescriptorView(context: Context, attributeSet: AttributeSet? = null) : + FrameLayout(context, attributeSet) { private var descriptor: Descriptor? = null + lateinit var gattViewDescriptorBinding: ViewGattDescriptorBinding constructor(context: Context, descriptor: Descriptor) : this(context) { this.descriptor = descriptor initView(descriptor) - if(descriptor.isPredefined) hideButtons() + if (descriptor.isPredefined) hideButtons() } init { - LayoutInflater.from(context).inflate(R.layout.view_gatt_descriptor, this, true) + gattViewDescriptorBinding = ViewGattDescriptorBinding.inflate(LayoutInflater.from(context),this,true) } private fun initView(descriptor: Descriptor) { - tv_descriptor_name.text = descriptor.name - tv_descriptor_uuid.text = buildBoldHeaderTextLine(context.getString(R.string.UUID_colon_space), descriptor.uuid?.getAsFormattedText()) - tv_descriptor_value.text = buildBoldHeaderTextLine(context.getString(R.string.value_colon_space), descriptor.value?.getAsFormattedText()) + + gattViewDescriptorBinding.tvDescriptorName.text = descriptor.name + gattViewDescriptorBinding.tvDescriptorUuid.text = buildBoldHeaderTextLine( + context.getString(R.string.UUID_colon_space), + descriptor.uuid?.getAsFormattedText() + ) + gattViewDescriptorBinding.tvDescriptorValue.text = buildBoldHeaderTextLine( + context.getString(R.string.value_colon_space), + descriptor.value?.getAsFormattedText() + ) showSelectedProperties(descriptor) } @@ -52,39 +63,42 @@ class GattDescriptorView(context: Context, attributeSet: AttributeSet? = null) : descriptor?.let { initView(it) } + gattViewDescriptorBinding.root.invalidate() } - private fun hideButtons() { - ib_copy.visibility = View.GONE - ib_remove.visibility = View.GONE - ib_edit.visibility = View.GONE + + gattViewDescriptorBinding.ibCopy.visibility = View.GONE + gattViewDescriptorBinding.ibRemove.visibility = View.GONE + gattViewDescriptorBinding.ibEdit.visibility = View.GONE } private fun hideAllProperties() { - tv_property_read.visibility = View.GONE - tv_property_write.visibility = View.GONE + gattViewDescriptorBinding.tvPropertyRead.visibility = View.GONE + + gattViewDescriptorBinding.tvPropertyWrite.visibility = View.GONE } private fun showSelectedProperties(descriptor: Descriptor) { hideAllProperties() descriptor.properties.apply { - if (containsKey(Property.READ)) tv_property_read.visibility = View.VISIBLE - if (containsKey(Property.WRITE)) tv_property_write.visibility = View.VISIBLE + gattViewDescriptorBinding.tvPropertyRead + if (containsKey(Property.READ)) gattViewDescriptorBinding.tvPropertyRead.visibility = View.VISIBLE + if (containsKey(Property.WRITE)) gattViewDescriptorBinding.tvPropertyWrite.visibility = View.VISIBLE } } fun setDescriptorListener(listener: DescriptorListener) { - ib_copy.setOnClickListener { + gattViewDescriptorBinding.ibCopy.setOnClickListener { descriptor?.let { listener.onCopyDescriptor(it) } } - ib_edit.setOnClickListener { + gattViewDescriptorBinding.ibEdit.setOnClickListener { descriptor?.let { listener.onEditDescriptor(it) } } - ib_remove.setOnClickListener { + gattViewDescriptorBinding.ibRemove.setOnClickListener { descriptor?.let { listener.onRemoveDescriptor(it) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/blinky/fragments/BlinkyFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/blinky/fragments/BlinkyFragment.kt index 39b1ea9e..a3609a64 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/blinky/fragments/BlinkyFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/blinky/fragments/BlinkyFragment.kt @@ -5,13 +5,18 @@ import android.view.View import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.FragmentBlinkyBinding import com.siliconlabs.bledemo.features.demo.blinky.models.LightState import com.siliconlabs.bledemo.features.demo.blinky.viewmodels.BlinkyViewModel -import kotlinx.android.synthetic.main.fragment_blinky.* + +//import kotlinx.android.synthetic.main.fragment_blinky.* class BlinkyFragment : Fragment(R.layout.fragment_blinky) { private lateinit var viewModel: BlinkyViewModel + private val layout by viewBinding(FragmentBlinkyBinding::bind) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -35,23 +40,24 @@ class BlinkyFragment : Fragment(R.layout.fragment_blinky) { } private fun switchLightOn() { - iv_light_bulb.setImageResource(R.drawable.light_on) + layout.ivLightBulb.setImageResource(R.drawable.light_on) } private fun switchLightOff() { - iv_light_bulb.setImageResource(R.drawable.light_off) + layout.ivLightBulb.setImageResource(R.drawable.light_off) } private fun setButtonImage(isPressed: Boolean) { + if (isPressed) { - iv_button.setImageResource(R.drawable.ic_button_on) + layout.ivButton.setImageResource(R.drawable.ic_button_on) } else { - iv_button.setImageResource(R.drawable.ic_button_off) + layout.ivButton.setImageResource(R.drawable.ic_button_off) } } private fun handleClickEvents() { - iv_light_bulb.setOnClickListener { + layout.ivLightBulb.setOnClickListener { viewModel.changeLightState() } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/connected_lighting/fragments/ConnectedLightingFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/connected_lighting/fragments/ConnectedLightingFragment.kt index 88d83a8d..9ab8edd8 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/connected_lighting/fragments/ConnectedLightingFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/connected_lighting/fragments/ConnectedLightingFragment.kt @@ -11,20 +11,28 @@ import com.siliconlabs.bledemo.features.demo.connected_lighting.activities.Conne import com.siliconlabs.bledemo.features.demo.connected_lighting.presenters.ConnectedLightingPresenter import com.siliconlabs.bledemo.features.demo.connected_lighting.models.TriggerSource import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.fragment_light.* +import com.siliconlabs.bledemo.databinding.FragmentLightBinding + +//import kotlinx.android.synthetic.main.fragment_light.* class ConnectedLightingFragment : Fragment(), ConnectedLightingPresenter.View { lateinit var presenter: ConnectedLightingPresenter + private lateinit var binding: FragmentLightBinding - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_light, container, false) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentLightBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) presenter = ConnectedLightingPresenter(this, activity as ConnectedLightingActivity) - btn_lightbulb.setOnClickListener { + binding.btnLightbulb.setOnClickListener { view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY) presenter.onLightClicked() } @@ -32,40 +40,43 @@ class ConnectedLightingFragment : Fragment(), ConnectedLightingPresenter.View { override fun showLightState(lightOn: Boolean) { if (lightOn) { - btn_lightbulb.setImageResource(R.drawable.light_on) - tv_light_status.setText(R.string.light_demo_on) + binding.btnLightbulb.setImageResource(R.drawable.light_on) + binding.tvLightStatus.setText(R.string.light_demo_on) } else { - btn_lightbulb.setImageResource(R.drawable.light_off) - tv_light_status.setText(R.string.light_demo_off) + binding.btnLightbulb.setImageResource(R.drawable.light_off) + binding.tvLightStatus.setText(R.string.light_demo_off) } } override fun showTriggerSourceDetails(source: TriggerSource?) { + if (isAdded && !isDetached && !isRemoving) { - if(source?.iconId == android.R.color.transparent) { - iv_light_change_source_logo.visibility = View.GONE + if (source?.iconId == android.R.color.transparent) { + binding.ivLightChangeSourceLogo.visibility = View.GONE } else { - iv_light_change_source_logo.setImageResource(source?.iconId!!) - iv_light_change_source_logo.visibility = View.VISIBLE + binding.ivLightChangeSourceLogo.setImageResource(source?.iconId!!) + binding.ivLightChangeSourceLogo.visibility = View.VISIBLE } - tv_light_change_source_name.text = getText(source.textResId) + + binding.tvLightChangeSourceName.text = getText(source.textResId) } } override fun showTriggerSourceAddress(sourceAddress: String, source: TriggerSource?) { + binding.tvLightChangeSource if (TriggerSource.UNKNOWN == source) { - tv_light_change_source.text = getString(R.string.light_demo_changed_source_unknown) + binding.tvLightChangeSource.text = getString(R.string.light_demo_changed_source_unknown) } else { - tv_light_change_source.text = StringBuffer(sourceAddress).reverse().toString() + binding.tvLightChangeSource.text = StringBuffer(sourceAddress).reverse().toString() } } override fun showDeviceDisconnectedDialog() { - AlertDialog.Builder(activity!!) - .setMessage(R.string.light_demo_connection_lost) - .setPositiveButton(R.string.light_demo_connection_lost_button) { dialog, which -> dialog.dismiss() } - .setOnDismissListener { presenter.leaveDemo() } - .create() - .show() + AlertDialog.Builder(requireActivity()) + .setMessage(R.string.light_demo_connection_lost) + .setPositiveButton(R.string.light_demo_connection_lost_button) { dialog, which -> dialog.dismiss() } + .setOnDismissListener { presenter.leaveDemo() } + .create() + .show() } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/activities/DevKitSensor917Activity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/activities/DevKitSensor917Activity.kt index 2b07a9af..c39947b2 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/activities/DevKitSensor917Activity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/activities/DevKitSensor917Activity.kt @@ -1,10 +1,15 @@ package com.siliconlabs.bledemo.features.demo.devkitsensor917.activities import android.annotation.SuppressLint +import android.content.Context import android.graphics.Color import android.graphics.drawable.ColorDrawable +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build import android.os.Bundle import android.view.MenuItem +import android.view.View import android.widget.Toast import androidx.annotation.DrawableRes import androidx.annotation.StringRes @@ -14,10 +19,10 @@ import androidx.fragment.app.Fragment import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.databinding.Activity917DevKitSensorLayoutBinding import com.siliconlabs.bledemo.features.demo.devkitsensor917.APIInterface -import com.siliconlabs.bledemo.features.demo.devkitsensor917.model.ScanResponse import com.siliconlabs.bledemo.features.demo.devkitsensor917.model.SensorsResponse import com.siliconlabs.bledemo.features.demo.devkitsensor917.utils.DevKitSensorChecker import com.siliconlabs.bledemo.features.demo.devkitsensor917.utils.DevKitSensorControl +import com.siliconlabs.bledemo.features.demo.devkitsensor917.utils.DevKitSensorSharedData import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomProgressDialog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope @@ -49,13 +54,23 @@ class DevKitSensor917Activity : AppCompatActivity() { actionBar!!.setHomeAsUpIndicator(R.drawable.matter_back) actionBar.setDisplayHomeAsUpEnabled(true) val ipAddress = intent.getStringExtra(IP_ADDRESS) - if (ipAddress != null) { - showProgressDialog(this.getString(R.string.dev_kit_progress_bar_message)) - println("IP ADDRESS Imported: $ipAddress") - initGrid(ipAddress) - GlobalScope.launch { - doInSensorBackground(ipAddress) + if (isNetworkAvailable(this)) { + if (ipAddress != null) { + binding.envGridPlace.visibility = View.VISIBLE + binding.envGrid.visibility = View.VISIBLE + binding.placeholder.visibility = View.GONE + showProgressDialog(this.getString(R.string.dev_kit_progress_bar_message)) + println("IP ADDRESS Imported: $ipAddress") + initGrid(ipAddress) + GlobalScope.launch { + doInSensorBackground(ipAddress) + } } + } else { + binding.envGridPlace.visibility = View.GONE + binding.envGrid.visibility = View.GONE + binding.placeholder.visibility = View.VISIBLE + Toast.makeText(this, "Please turn the WiFi Setting", Toast.LENGTH_SHORT).show() } } @@ -84,7 +99,7 @@ class DevKitSensor917Activity : AppCompatActivity() { removeProgress() runOnUiThread { Toast.makeText( - baseContext, + baseContext, "API All Sensors Response failed", Toast.LENGTH_SHORT ).show() @@ -120,9 +135,8 @@ class DevKitSensor917Activity : AppCompatActivity() { getString(getTileDescription(it.key)), ContextCompat.getDrawable(this.context, getTileIcon(it.key)) ).also { - - addView(it) - it.setListener(it.tag, ipAddress) + addView(it.sensorDemoGridItemBinding.root) + it.setListener(DevKitSensorSharedData.sensorDescription.toString(), ipAddress) } @@ -179,6 +193,29 @@ class DevKitSensor917Activity : AppCompatActivity() { } + private fun isNetworkAvailable(context: Context?): Boolean { + if (context == null) return false + val connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val capabilities = + connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + if (capabilities != null) { + when { + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> { + return true + } + } + } + } else { + val activeNetworkInfo = connectivityManager.activeNetworkInfo + if (activeNetworkInfo != null && activeNetworkInfo.isConnected) { + return true + } + } + return false + } + interface ResponseListener { fun fetchData(response: SensorsResponse?) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/model/sensorsResponse.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/model/sensorsResponse.kt index 6be5cf5f..44c0eecb 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/model/sensorsResponse.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/model/sensorsResponse.kt @@ -1,5 +1,6 @@ package com.siliconlabs.bledemo.features.demo.devkitsensor917.model + data class SensorsResponse( val led: LEDResponse, val light: AmbientLightResponse, @@ -8,4 +9,4 @@ data class SensorsResponse( val gyroscope: AccelerometerGyroScopeResponse, val humidity: HumidityResponse, val microphone: MicrophoneResponse -) +) \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/utils/DevKitSensorControl.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/utils/DevKitSensorControl.kt index 575d4f49..f48bfee1 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/utils/DevKitSensorControl.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/utils/DevKitSensorControl.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.app.Dialog import android.content.Context import android.graphics.drawable.Drawable +import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Button @@ -14,17 +15,13 @@ import android.widget.Toast import androidx.core.content.ContextCompat import androidx.gridlayout.widget.GridLayout import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.SensorDemoGridItemBinding import com.siliconlabs.bledemo.features.demo.devkitsensor917.APIInterface import com.siliconlabs.bledemo.features.demo.devkitsensor917.activities.DevKitSensor917Activity import com.siliconlabs.bledemo.features.demo.devkitsensor917.model.AccelerometerGyroScopeResponse -import com.siliconlabs.bledemo.features.demo.devkitsensor917.model.AmbientLightResponse -import com.siliconlabs.bledemo.features.demo.devkitsensor917.model.HumidityResponse import com.siliconlabs.bledemo.features.demo.devkitsensor917.model.LEDResponse import com.siliconlabs.bledemo.features.demo.devkitsensor917.model.LEDStatusResponse import com.siliconlabs.bledemo.features.demo.devkitsensor917.model.SensorsResponse -import com.siliconlabs.bledemo.features.demo.devkitsensor917.model.TempResponse -import kotlinx.android.synthetic.main.environmentdemo_tile.view.env_description -import kotlinx.android.synthetic.main.environmentdemo_tile.view.env_icon import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -62,18 +59,19 @@ open class DevKitSensorControl( constructor(context: Context) : this(context, null, null) - private val tileView: View = inflate(context, R.layout.sensor_demo_grid_item, this) + var sensorDemoGridItemBinding: SensorDemoGridItemBinding = + SensorDemoGridItemBinding.inflate(LayoutInflater.from(context)) fun setListener(tag: Any, ipAddress: String) { val code = tag.toString() - tileView.setOnClickListener { + sensorDemoGridItemBinding.cardviewEnvTile.setOnClickListener { when (code) { - temperature -> showTemperatureDialog(code, ipAddress) - humidity -> showHumidityDialog(code, ipAddress) - ambientLight -> showAmbientLightDialog(code, ipAddress) - microphone -> showMicrophoneDialog(code, ipAddress) + TEMPERATURE -> showTemperatureDialog(code, ipAddress) + HUMIDITY -> showHumidityDialog(code, ipAddress) + AMBIENT_LIGHT -> showAmbientLightDialog(code, ipAddress) + MICROPHONE -> showMicrophoneDialog(code, ipAddress) LED -> showLEDControlDialog(code, ipAddress) - motion -> showMotionDialog(code, ipAddress) + MOTION -> showMotionDialog(code, ipAddress) } } } @@ -88,12 +86,12 @@ open class DevKitSensorControl( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ) - val header = devkitSensorDialog.findViewById(R.id.header) as TextView - val yesBtn = devkitSensorDialog.findViewById(R.id.yes_opt) as TextView - val noBtn = devkitSensorDialog.findViewById(R.id.no_opt) as TextView - val orientationX = devkitSensorDialog.findViewById(R.id.orient_x) as TextView - val orientationY = devkitSensorDialog.findViewById(R.id.orientation_y) as TextView - val orientationZ = devkitSensorDialog.findViewById(R.id.orientation_z) as TextView + val header: TextView = devkitSensorDialog.findViewById(R.id.header) + val yesBtn: TextView = devkitSensorDialog.findViewById(R.id.yes_opt) + val noBtn: TextView = devkitSensorDialog.findViewById(R.id.no_opt) + val orientationX: TextView = devkitSensorDialog.findViewById(R.id.orient_x) + val orientationY: TextView = devkitSensorDialog.findViewById(R.id.orientation_y) + val orientationZ: TextView = devkitSensorDialog.findViewById(R.id.orientation_z) val degreeString = context.getString(R.string.motion_orientation_degree) if (gyroResponse != null && gyroResponse!!.x.isNotEmpty()) { orientationX.text = String.format(degreeString, gyroResponse!!.x.toFloat()) @@ -112,9 +110,9 @@ open class DevKitSensorControl( } val accelerationString = context.getString(R.string.motion_acceleration_g) - val acceloX = devkitSensorDialog.findViewById(R.id.accelo_x) as TextView - val acceloY = devkitSensorDialog.findViewById(R.id.accelo_y) as TextView - val acceloZ = devkitSensorDialog.findViewById(R.id.accelo_z) as TextView + val acceloX: TextView = devkitSensorDialog.findViewById(R.id.accelo_x) + val acceloY: TextView = devkitSensorDialog.findViewById(R.id.accelo_y) + val acceloZ: TextView = devkitSensorDialog.findViewById(R.id.accelo_z) if (acceloResponse != null && acceloResponse!!.x.isNotEmpty()) { acceloX.text = String.format(accelerationString, acceloResponse!!.x.toFloat()) } else { @@ -151,7 +149,7 @@ open class DevKitSensorControl( apiJob?.cancel() } } - header.text = title + space + context.getString(R.string.title_sensor) + header.text = title + SPACE + context.getString(R.string.title_sensor) devkitSensorDialog.show() apiJob = CoroutineScope(Dispatchers.IO).launch { while (true) { @@ -259,19 +257,19 @@ open class DevKitSensorControl( ViewGroup.LayoutParams.WRAP_CONTENT ) - val header = devKitSensorDialog.findViewById(R.id.header) as TextView - val subTitle = devKitSensorDialog.findViewById(R.id.subTitle) as TextView - val yesBtn = devKitSensorDialog.findViewById(R.id.yes_opt) as TextView - val noBtn = devKitSensorDialog.findViewById(R.id.no_opt) as TextView - val image = devKitSensorDialog.findViewById(R.id.image) as ImageView - val dataHolder = devKitSensorDialog.findViewById(R.id.txt_value) as TextView + val header: TextView = devKitSensorDialog.findViewById(R.id.header) + val subTitle: TextView = devKitSensorDialog.findViewById(R.id.subTitle) + val yesBtn: TextView = devKitSensorDialog.findViewById(R.id.yes_opt) + val noBtn: TextView = devKitSensorDialog.findViewById(R.id.no_opt) + val image: ImageView = devKitSensorDialog.findViewById(R.id.image) + val dataHolder: TextView = devKitSensorDialog.findViewById(R.id.txt_value) dataHolder.text = context.getString(R.string.matter_init_value) // println("tempResponse :${tempResponse!!.temperature_celcius}") if (tempResponse != null && tempResponse!!.isNotEmpty()) { - dataHolder.text = tempResponse + " ℃" + dataHolder.text = "$tempResponse ℃" } image.setImageResource(R.drawable.icon_temp) - header.text = title + space + context.getString(R.string.title_sensor) + header.text = title + SPACE + context.getString(R.string.title_sensor) subTitle.text = title yesBtn.setOnClickListener { yesBtn.isEnabled = false @@ -436,17 +434,19 @@ open class DevKitSensorControl( } init { - tileView.setTag(description) - tileView.apply { - env_description.text = description - env_icon.setImageDrawable(icon) + DevKitSensorSharedData.sensorDescription = description + sensorDemoGridItemBinding.cardviewEnvTile.apply { + sensorDemoGridItemBinding.envDescription.text = description + sensorDemoGridItemBinding.envIcon.setImageDrawable(icon) } + layoutParams = GridLayout.LayoutParams( GridLayout.spec(GridLayout.UNDEFINED, 1f), GridLayout.spec(GridLayout.UNDEFINED, 1f) ).apply { width = 0 } + } @SuppressLint("SetTextI18n") @@ -458,15 +458,15 @@ open class DevKitSensorControl( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ) - val header = devKitSensorDialog.findViewById(R.id.header) as TextView - val subTitle = devKitSensorDialog.findViewById(R.id.subTitle) as TextView - val yesBtn = devKitSensorDialog.findViewById(R.id.yes_opt) as TextView - val noBtn = devKitSensorDialog.findViewById(R.id.no_opt) as TextView - val image = devKitSensorDialog.findViewById(R.id.image) as ImageView + val header: TextView = devKitSensorDialog.findViewById(R.id.header) + val subTitle: TextView = devKitSensorDialog.findViewById(R.id.subTitle) + val yesBtn: TextView = devKitSensorDialog.findViewById(R.id.yes_opt) + val noBtn: TextView = devKitSensorDialog.findViewById(R.id.no_opt) + val image: ImageView = devKitSensorDialog.findViewById(R.id.image) val microphoneHolder = devKitSensorDialog.findViewById(R.id.txt_value) as TextView microphoneHolder.text = context.getString(R.string.dev_kit_sensor_micro_init_value) image.setImageResource(R.drawable.icon_sound) - header.text = title + space + context.getString(R.string.title_sensor) + header.text = title + SPACE + context.getString(R.string.title_sensor) subTitle.text = title yesBtn.setOnClickListener { CoroutineScope(Dispatchers.IO).launch { @@ -499,20 +499,20 @@ open class DevKitSensorControl( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ) - val header = devKitSensorDialog.findViewById(R.id.header) as TextView - val subTitle = devKitSensorDialog.findViewById(R.id.subTitle) as TextView - val yesBtn = devKitSensorDialog.findViewById(R.id.yes_opt) as TextView - val noBtn = devKitSensorDialog.findViewById(R.id.no_opt) as TextView - val image = devKitSensorDialog.findViewById(R.id.image) as ImageView - val abiHolder = devKitSensorDialog.findViewById(R.id.txt_value) as TextView + val header: TextView = devKitSensorDialog.findViewById(R.id.header) + val subTitle: TextView = devKitSensorDialog.findViewById(R.id.subTitle) + val yesBtn: TextView = devKitSensorDialog.findViewById(R.id.yes_opt) + val noBtn: TextView = devKitSensorDialog.findViewById(R.id.no_opt) + val image: ImageView = devKitSensorDialog.findViewById(R.id.image) + val abiHolder: TextView = devKitSensorDialog.findViewById(R.id.txt_value) abiHolder.text = context.getString(R.string.dev_kit_sensor_ambient_init_value) if (ambiResponse != null && ambiResponse!!.isNotEmpty()) { - abiHolder.text = ambiResponse + " lx" + abiHolder.text = "$ambiResponse lx" } abiHolder.textSize = 50F image.setImageResource(R.drawable.icon_light) - header.text = title + space + context.getString(R.string.title_sensor) + header.text = title + SPACE + context.getString(R.string.title_sensor) subTitle.text = title yesBtn.setOnClickListener { @@ -579,18 +579,18 @@ open class DevKitSensorControl( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ) - val header = devKitSensorDialog.findViewById(R.id.header) as TextView - val subTitle = devKitSensorDialog.findViewById(R.id.subTitle) as TextView - val yesBtn = devKitSensorDialog.findViewById(R.id.yes_opt) as TextView - val noBtn = devKitSensorDialog.findViewById(R.id.no_opt) as TextView - onLEDBtn = devKitSensorDialog.findViewById(R.id.onButton) as Button - offLEDBtn = devKitSensorDialog.findViewById(R.id.offButton) as Button - redLEDBtn = devKitSensorDialog.findViewById(R.id.redButton) as Button - greenLEDBtn = devKitSensorDialog.findViewById(R.id.greenButton) as Button - blueLEDBtn = devKitSensorDialog.findViewById(R.id.blueButton) as Button - ledImageStatus = devKitSensorDialog.findViewById(R.id.imageLight) as ImageView - - header.text = title + space + context.getString(R.string.title_control) + val header: TextView = devKitSensorDialog.findViewById(R.id.header) + val subTitle: TextView = devKitSensorDialog.findViewById(R.id.subTitle) + val yesBtn: TextView = devKitSensorDialog.findViewById(R.id.yes_opt) + val noBtn: TextView = devKitSensorDialog.findViewById(R.id.no_opt) + onLEDBtn = devKitSensorDialog.findViewById(R.id.onButton)!! + offLEDBtn = devKitSensorDialog.findViewById(R.id.offButton)!! + redLEDBtn = devKitSensorDialog.findViewById(R.id.redButton)!! + greenLEDBtn = devKitSensorDialog.findViewById(R.id.greenButton)!! + blueLEDBtn = devKitSensorDialog.findViewById(R.id.blueButton)!! + ledImageStatus = devKitSensorDialog.findViewById(R.id.imageLight)!! + + header.text = title + SPACE + context.getString(R.string.title_control) subTitle.text = title subTitle.visibility = View.GONE @@ -687,8 +687,7 @@ open class DevKitSensorControl( if (btnBlueStatus) { onLEDBtn.setBackgroundResource(R.drawable.button_background_soft_black_box) blueLEDBtn.setBackgroundResource(R.drawable.button_background_blue_box) - } else { - offLEDBtn.setBackgroundResource(R.drawable.button_background_grey_box) + }else{ blueLEDBtn.setBackgroundResource(R.drawable.button_background_grey_box) } if (btnRedStatus && btnGreenStatus && btnBlueStatus) { @@ -740,7 +739,7 @@ open class DevKitSensorControl( val retro = Retrofit.Builder().baseUrl(url) .addConverterFactory(GsonConverterFactory.create()).build() val ledStatus = retro.create(APIInterface::class.java) - val params: MutableMap = HashMap() + val params: MutableMap = HashMap() var statusRed: String = OFF if (red) { statusRed = ON @@ -775,7 +774,7 @@ open class DevKitSensorControl( setBtnColorBackground() } } else { - + //ignore } } @@ -902,7 +901,7 @@ open class DevKitSensorControl( btnBlueStatus = false btnGreenStatus = false setOffBtnBackground() - imageForLightOn(view, false, false, false) + imageForLightOn(view, btnRedStatus, btnBlueStatus, btnGreenStatus) Toast.makeText( context, "Failed to connect. Check your Wi-Fi connection", @@ -1024,18 +1023,18 @@ open class DevKitSensorControl( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ) - val header = devKitSensorDialog.findViewById(R.id.header) as TextView - val subTitle = devKitSensorDialog.findViewById(R.id.subTitle) as TextView - val yesBtn = devKitSensorDialog.findViewById(R.id.yes_opt) as TextView - val noBtn = devKitSensorDialog.findViewById(R.id.no_opt) as TextView - val image = devKitSensorDialog.findViewById(R.id.image) as ImageView - val humidityHolder = devKitSensorDialog.findViewById(R.id.txt_value) as TextView + val header: TextView = devKitSensorDialog.findViewById(R.id.header) + val subTitle: TextView = devKitSensorDialog.findViewById(R.id.subTitle) + val yesBtn: TextView = devKitSensorDialog.findViewById(R.id.yes_opt) + val noBtn: TextView = devKitSensorDialog.findViewById(R.id.no_opt) + val image: ImageView = devKitSensorDialog.findViewById(R.id.image) + val humidityHolder: TextView = devKitSensorDialog.findViewById(R.id.txt_value) humidityHolder.text = context.getString(R.string.dev_kit_sensor_humidity_init_value) if (humiResponse != null && humiResponse!!.isNotEmpty()) { - humidityHolder.text = humiResponse + " %" + humidityHolder.text = "$humiResponse %" } image.setImageResource(R.drawable.icon_environment) - header.text = title + space + context.getString(R.string.title_sensor) + header.text = title + SPACE + context.getString(R.string.title_sensor) subTitle.text = title yesBtn.setOnClickListener { yesBtn.isEnabled = false @@ -1063,36 +1062,31 @@ open class DevKitSensorControl( override fun fetchData(response: SensorsResponse?) { - //temperatureResponse = tempResponse - println("----------------${response!!.led.blue}") if (response != null) { //Temp - if (response.temperature != null && response.temperature.temperature_celcius.isNotEmpty()) { - println("----------------${response.temperature.temperature_celcius}") + if (response.temperature.temperature_celcius.isNotEmpty()) { tempResponse = response.temperature.temperature_celcius } //Humi - if (response.humidity != null && response.humidity.humidity_percentage.isNotEmpty()) { - println("----------------${response.humidity.humidity_percentage}") + if (response.humidity.humidity_percentage.isNotEmpty()) { humiResponse = response.temperature.temperature_celcius } //Ambi - if (response.light != null && response.light.ambient_light_lux.isNotEmpty()) { - println("----------------${response.light.ambient_light_lux}") + if (response.light.ambient_light_lux.isNotEmpty()) { ambiResponse = response.light.ambient_light_lux } } - } + companion object { - const val space = " " - const val temperature = "Temperature" - const val humidity = "Humidity" - const val ambientLight = "Ambient Light" - const val microphone = "Microphone" + const val SPACE = " " + const val TEMPERATURE = "Temperature" + const val HUMIDITY = "Humidity" + const val AMBIENT_LIGHT = "Ambient Light" + const val MICROPHONE = "Microphone" const val LED = "LED" - const val motion = "Motion" + const val MOTION = "Motion" const val TIME_OUT = 2000L const val OFF = "off" const val ON = "on" diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/utils/DevKitSensorSharedData.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/utils/DevKitSensorSharedData.kt new file mode 100644 index 00000000..2a4f6c6a --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/devkitsensor917/utils/DevKitSensorSharedData.kt @@ -0,0 +1,5 @@ +package com.siliconlabs.bledemo.features.demo.devkitsensor917.utils + +object DevKitSensorSharedData { + var sensorDescription: String? = null +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/health_thermometer/activities/HealthThermometerActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/health_thermometer/activities/HealthThermometerActivity.kt index 2d3ed058..0ceff7ed 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/health_thermometer/activities/HealthThermometerActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/health_thermometer/activities/HealthThermometerActivity.kt @@ -7,6 +7,8 @@ import android.content.Intent import android.graphics.Typeface import android.os.Bundle import android.text.TextUtils +import android.view.LayoutInflater +import android.widget.TextView import com.siliconlabs.bledemo.home_screen.dialogs.SelectDeviceDialog import com.siliconlabs.bledemo.bluetooth.ble.GattCharacteristic import com.siliconlabs.bledemo.bluetooth.ble.GattService @@ -15,9 +17,11 @@ import com.siliconlabs.bledemo.features.demo.health_thermometer.models.Temperatu import com.siliconlabs.bledemo.features.demo.health_thermometer.models.TemperatureReading.HtmType import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.base.activities.BaseDemoActivity +import com.siliconlabs.bledemo.databinding.ActivityThermometerBinding import com.siliconlabs.bledemo.utils.BLEUtils.setNotificationForCharacteristic import com.siliconlabs.bledemo.utils.Notifications -import kotlinx.android.synthetic.main.activity_thermometer.* + +//import kotlinx.android.synthetic.main.activity_thermometer.* @SuppressLint("MissingPermission") class HealthThermometerActivity : BaseDemoActivity() { @@ -26,6 +30,7 @@ class HealthThermometerActivity : BaseDemoActivity() { private var currentReading: TemperatureReading? = null private var currentType: TemperatureReading.Type? = null + private lateinit var binding: ActivityThermometerBinding private val gattCallback: TimeoutGattCallback = object : TimeoutGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { @@ -53,13 +58,18 @@ class HealthThermometerActivity : BaseDemoActivity() { } } if (startNotificationForCharacteristicFromHere) { - setNotificationForCharacteristic(gatt, GattService.HealthThermometer, - GattCharacteristic.Temperature, - Notifications.INDICATE) + setNotificationForCharacteristic( + gatt, GattService.HealthThermometer, + GattCharacteristic.Temperature, + Notifications.INDICATE + ) } } - override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { + override fun onCharacteristicChanged( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic + ) { super.onCharacteristicChanged(gatt, characteristic) if (GattCharacteristic.fromUuid(characteristic.uuid) == GattCharacteristic.Temperature) { val reading = TemperatureReading.fromCharacteristic(characteristic) @@ -70,40 +80,56 @@ class HealthThermometerActivity : BaseDemoActivity() { } runOnUiThread { setCurrentReading(reading) - connection_bar_text.text = getString(R.string.demo_connected_to, deviceName) + binding.connectionBarText.text = + getString(R.string.demo_connected_to, deviceName) } } } - override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { + override fun onCharacteristicRead( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { super.onCharacteristicRead(gatt, characteristic, status) if (GattCharacteristic.fromUuid(characteristic.uuid) == GattCharacteristic.TemperatureType) { - htmType = HtmType.values()[characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0)] - setNotificationForCharacteristic(gatt, GattService.HealthThermometer, - GattCharacteristic.Temperature, - Notifications.INDICATE) + htmType = HtmType.values()[characteristic.getIntValue( + BluetoothGattCharacteristic.FORMAT_UINT8, + 0 + )] + setNotificationForCharacteristic( + gatt, GattService.HealthThermometer, + GattCharacteristic.Temperature, + Notifications.INDICATE + ) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_thermometer) + binding = ActivityThermometerBinding.inflate(LayoutInflater.from(this)) + setContentView(binding.root) + //setContentView(R.layout.activity_thermometer) - thermo_large_temperature.setFontFamily("sans-serif-thin", Typeface.NORMAL) - type_switch.setOnCheckedChangeListener { _, isChecked -> onTabClick(isChecked) } + binding.thermoLargeTemperature.setFontFamily("sans-serif-thin", Typeface.NORMAL) + binding.typeSwitch.setOnCheckedChangeListener { _, isChecked -> onTabClick(isChecked) } } override fun onResume() { super.onResume() - if (serviceHasBeenSet && service == null || service != null && !service?.isGattConnected(connectionAddress)!!) { + if (serviceHasBeenSet && service == null || service != null && !service?.isGattConnected( + connectionAddress + )!! + ) { onDeviceDisconnected() } } private fun onTabClick(isFahrenheitUnit: Boolean) { - currentType = if (isFahrenheitUnit) TemperatureReading.Type.FAHRENHEIT else TemperatureReading.Type.CELSIUS - thermo_large_temperature.setCurrentType(currentType) + currentType = + if (isFahrenheitUnit) TemperatureReading.Type.FAHRENHEIT else TemperatureReading.Type.CELSIUS + binding.thermoLargeTemperature.setCurrentType(currentType) } fun setCurrentReading(temperatureReading: TemperatureReading?) { @@ -113,14 +139,17 @@ class HealthThermometerActivity : BaseDemoActivity() { private fun refreshUi() { if (currentReading != null) { - thermo_large_temperature?.setTemperature(currentReading) - thermo_type_value_text?.text = getString(currentReading?.htmType?.nameResId!!) - thermo_type_value?.text = getString(R.string.temperature_type, - getString(currentReading?.htmType?.nameResId!!)) - thermo_large_time_text?.text = currentReading?.getFormattedTime() + binding.thermoLargeTemperature.setTemperature(currentReading) + binding.thermoTypeValueText.text = getString(currentReading?.htmType?.nameResId!!) + binding.thermoTypeValue.text = getString(R.string.temperature_type, + getString(currentReading?.htmType?.nameResId!!)) + + binding.thermoLargeTimeText.text = currentReading?.getFormattedTime() } } + //private fun textView(): TextView = binding.thermoTypeValue.text + override fun onBluetoothServiceBound() { serviceHasBeenSet = true service?.registerGattCallback(true, gattCallback) @@ -130,7 +159,8 @@ class HealthThermometerActivity : BaseDemoActivity() { override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) //When you switch thermometers, this activity should get a new intent - at this point, we hide the dialog - val dialog = supportFragmentManager.findFragmentByTag("select_device_tag") as SelectDeviceDialog? + val dialog = + supportFragmentManager.findFragmentByTag("select_device_tag") as SelectDeviceDialog? if (dialog != null && dialog.isVisible) { dialog.dismiss() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/activities/MatterDemoActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/activities/MatterDemoActivity.kt index 7968cf26..de2320cc 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/activities/MatterDemoActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/activities/MatterDemoActivity.kt @@ -12,8 +12,7 @@ import chip.devicecontroller.ChipDeviceController import chip.devicecontroller.GetConnectedDeviceCallbackJni import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.databinding.ActivityMatterDemoBinding -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterConnectFragment -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterConnectFragment.Companion.DIA_INPUT_NTW_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterDishwasherFragment import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterDoorFragment import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterOccupancySensorFragment @@ -22,26 +21,23 @@ import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScanner import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterThermostatFragment import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterWifiInputDialogFragment import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterWindowCoverFragment -import com.siliconlabs.bledemo.features.demo.matter_demo.model.CHIPDeviceInfo import com.siliconlabs.bledemo.features.demo.matter_demo.model.MatterScannedResultModel import com.siliconlabs.bledemo.features.demo.matter_demo.model.ProvisionNetworkType import com.siliconlabs.bledemo.features.demo.matter_demo.utils.ChipClient import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomProgressDialog import com.siliconlabs.bledemo.features.demo.matter_demo.utils.SharedPrefsUtils -import kotlinx.android.synthetic.main.activity_matter_demo.refresh -import kotlinx.android.synthetic.main.activity_matter_demo.scanQRCode +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber +import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine class MatterDemoActivity : AppCompatActivity(), MatterScannerFragment.CallBack, - MatterConnectFragment.Callback, MatterScannedResultFragment.Callback, MatterThermostatFragment.CallBackHandler, MatterLightFragment.CallBackHandler, @@ -53,18 +49,18 @@ class MatterDemoActivity : AppCompatActivity(), private var currentFragment: Fragment? = null private lateinit var binding: ActivityMatterDemoBinding - private var deviceInfo: CHIPDeviceInfo? = null private var scannedDeviceList = ArrayList() private lateinit var mPrefs: SharedPreferences - private var chipDeviceList: ArrayList = ArrayList() private lateinit var deviceController: ChipDeviceController private var customProgressDialog: CustomProgressDialog? = null private var networkType: ProvisionNetworkType? = null private var shouldContinueOperation = true private var count: Int = 0 + + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Timber.tag(TAG).e( " oncreate") + Timber.tag(TAG).e(" onCreate") binding = ActivityMatterDemoBinding.inflate(layoutInflater) mPrefs = this.getSharedPreferences("your_preference_name", MODE_PRIVATE) setSupportActionBar(binding.toolbar) @@ -72,24 +68,23 @@ class MatterDemoActivity : AppCompatActivity(), deviceController = ChipClient.getDeviceController(this) val actionBar = supportActionBar - actionBar!!.setHomeAsUpIndicator(R.drawable.matter_back) - actionBar.setDisplayHomeAsUpEnabled(true) - - scanQRCode.setOnClickListener { + if (actionBar != null) { + actionBar.setHomeAsUpIndicator(R.drawable.matter_back) + actionBar.setDisplayHomeAsUpEnabled(true) + } + binding.scanQRCode.setOnClickListener { val fragment = MatterScannerFragment.newInstance() showFragment( - fragment, true, fragment::class.java.simpleName, - fragment::class.java.simpleName + fragment, fragment::class.java.simpleName ) } - - refresh.setOnClickListener { + binding.refresh.setOnClickListener { count = 0 if (SharedPrefsUtils.retrieveSavedDevices(mPrefs).size > 0) { showMatterProgressDialog(getString(R.string.matter_device_status)) - GlobalScope.launch { - val resultq = performLongRunningOperation() - if (resultq) { + CoroutineScope(Dispatchers.Default).launch { + val resultInfo = performLongRunningOperation() + if (resultInfo) { println("Operation was successful") removeProgress() scannedDeviceList = SharedPrefsUtils.retrieveSavedDevices(mPrefs) @@ -106,7 +101,7 @@ class MatterDemoActivity : AppCompatActivity(), } private fun startPeriodictFunction() { - GlobalScope.launch(Dispatchers.Default) { + CoroutineScope(Dispatchers.Default).launch { while (shouldContinueOperation) { performLongRunningOperation() // Call your long-running operation delay(DELAY_TIMEOUT) @@ -140,27 +135,28 @@ class MatterDemoActivity : AppCompatActivity(), customProgressDialog = CustomProgressDialog(this) customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) customProgressDialog!!.setMessage(message) + customProgressDialog!!.setCanceledOnTouchOutside(false) customProgressDialog!!.show() } override fun onResume() { super.onResume() - Timber.tag(TAG).e( " on resume") + Timber.tag(TAG).e(" on resume") count = 0 } override fun onStart() { super.onStart() - Timber.tag(TAG).e( " onStart") + Timber.tag(TAG).e(" onStart") } - private suspend fun performLongRunningOperation(): Boolean { - GlobalScope.launch { + private fun performLongRunningOperation(): Boolean { + CoroutineScope(Dispatchers.IO).launch { val savedDevices = SharedPrefsUtils.retrieveSavedDevices(mPrefs) - for ((index, item) in savedDevices.withIndex()) { + for (item in savedDevices) { getStatus(item.deviceId) } } @@ -173,12 +169,12 @@ class MatterDemoActivity : AppCompatActivity(), println("Matter Total device found $savedDeviceCount") try { + // suspendCoroutine { continuation -> suspendCoroutine { continuation -> deviceController.getConnectedDevicePointer(deviceId, object : GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback { override fun onDeviceConnected(devicePointer: Long) { - //Timber.tag(TAG).e("-------------------------------") Timber.tag(TAG).e("devicePointer $devicePointer") SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) if (currentFragment != null) { @@ -193,17 +189,17 @@ class MatterDemoActivity : AppCompatActivity(), if (count >= savedDeviceCount) { removeProgress() } + // continuation.resume(Unit) } override fun onConnectionFailure( nodeId: Long, error: java.lang.Exception? ) { - - //Timber.tag(TAG).e("-------------------------------") Timber.tag(TAG).e("nodeId $nodeId Error $error") SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) - currentFragment = supportFragmentManager.findFragmentById(R.id.matter_container) + currentFragment = + supportFragmentManager.findFragmentById(R.id.matter_container) if (currentFragment != null) { when (currentFragment) { is MatterScannedResultFragment -> { @@ -229,7 +225,6 @@ class MatterDemoActivity : AppCompatActivity(), } - override fun onDestroy() { super.onDestroy() stopPeriodicOperation() @@ -246,8 +241,7 @@ class MatterDemoActivity : AppCompatActivity(), } private fun showFragment( - fragment: Fragment, addToBackStack: Boolean = true, - fragmentName: String? = null, tag: String? = null, + fragment: Fragment, tag: String? = null, ) { val fManager = supportFragmentManager val fTransaction = fManager.beginTransaction() @@ -259,32 +253,12 @@ class MatterDemoActivity : AppCompatActivity(), } - override fun onChipDeviceInfoReceived(deviceInfo: CHIPDeviceInfo, ntwInputType: String) { - Timber.tag(TAG).e(" onChipDeviceInfoReceived") - deviceInfo.networkType = ntwInputType - this.deviceInfo = deviceInfo - chipDeviceList.add(deviceInfo) - - val bundle = Bundle() - bundle.putParcelable(ARG_DEVICE_INFO, deviceInfo) - bundle.putString(DIA_INPUT_NTW_TYPE, ntwInputType) - val fragment = MatterConnectFragment.newInstance() - fragment.arguments = bundle - showFragment( - fragment, true, fragment::class.java.simpleName, - fragment::class.java.simpleName - ) - } - override fun onSaveInstanceState(outState: Bundle) { outState.putString(MatterScannerFragment.ARG_PROVISION_NETWORK_TYPE, networkType?.name) super.onSaveInstanceState(outState) } - override fun setNetworkType(type: ProvisionNetworkType) { - //ignore - } override fun onCommissionCompleteLoadData(matterScannedResultModel: MatterScannedResultModel) { Timber.tag(TAG).e("on commission complete add $matterScannedResultModel ") @@ -352,7 +326,18 @@ class MatterDemoActivity : AppCompatActivity(), this.finish() - } else if (supportFragmentManager.backStackEntryCount > 0) { + }else if (currentFragment != null && currentFragment is MatterDishwasherFragment) { + + val fragment = supportFragmentManager.findFragmentById(R.id.matter_container) as? MatterDishwasherFragment + + fragment?.let { + // You can access the DataBinding object here + // Example: Change the text of the TextView inside the fragment when back is pressed + it.handleDishwasherBackNavigationUI() + } + + } + else if (supportFragmentManager.backStackEntryCount > 0) { supportFragmentManager.popBackStack() } else { this.finish() @@ -365,10 +350,10 @@ class MatterDemoActivity : AppCompatActivity(), } companion object { - private const val ARG_DEVICE_INFO = "device_info" private const val TAG = "MatterDemoActivity" private const val ARG_DEVICE_LIST = "device_list" private const val ARG_DEVICE_MODEL = "device_model" private const val DELAY_TIMEOUT = 8000L + const val MATTER_PREF = "your_preference_name" } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/adapters/MatterScannedResultAdapter.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/adapters/MatterScannedResultAdapter.kt index fe06c239..3ae188dc 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/adapters/MatterScannedResultAdapter.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/adapters/MatterScannedResultAdapter.kt @@ -1,12 +1,10 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.adapters import android.content.Context -import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView -import com.daimajia.swipe.SimpleSwipeListener import com.daimajia.swipe.SwipeLayout import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.databinding.MatterScannedListItemBinding @@ -38,37 +36,38 @@ class MatterScannedResultAdapter( holder.itemView.isEnabled = true holder.itemView.isClickable = true - holder.itemView.alpha = 1.0f // Set alpha to 1 for enabled rows + holder.binding.root.alpha = 1.0f // Set alpha to 1 for enabled rows } else { - holder.binding.imageview.setColorFilter( + holder.binding.root.alpha = 0.5f + holder.binding.imageView.setColorFilter( ContextCompat.getColor(context, R.color.silabs_dark_gray_icon), android.graphics.PorterDuff.Mode.SRC_IN ) holder.itemView.isEnabled = false holder.itemView.isClickable = false - holder.itemView.alpha = 0.5f // Set alpha to 0.5 for disabled rows - holder.itemView.setBackgroundColor(ContextCompat.getColor(context, R.color.silabs_inactive_light)); - holder.binding.cardView.setBackgroundColor(ContextCompat.getColor(context, R.color.silabs_inactive_light)); - holder.binding.itemViewHolder.setBackgroundColor(ContextCompat.getColor(context, R.color.silabs_inactive_light)); - holder.binding.imageview.setBackgroundColor(ContextCompat.getColor(context, R.color.silabs_inactive_light)); - + // Set alpha to 0.5 for disabled rows +// holder.itemView.setBackgroundColor(ContextCompat.getColor(context, R.color.silabs_inactive_light)); +// holder.binding.cardView.setBackgroundColor(ContextCompat.getColor(context, R.color.silabs_inactive_light)); +// holder.binding.itemViewHolder.setBackgroundColor(ContextCompat.getColor(context, R.color.silabs_inactive_light)); +// holder.binding.imageView.setBackgroundColor(ContextCompat.getColor(context, R.color.silabs_inactive_light)); } - // holder.binding.imgDelete.setVisibility(View.GONE); - holder.binding.swipe.setShowMode(SwipeLayout.ShowMode.PullOut); + holder.binding.swipe.setShowMode(SwipeLayout.ShowMode.LayDown); holder.binding.swipe.addDrag( SwipeLayout.DragEdge.Right, holder.binding.swipe.findViewById(R.id.bottom_wrapper) ) - //viewHolder.swipeLayout.addDrag(SwipeLayout.DragEdge.Right, viewHolder.swipeLayout.findViewById(R.id.bottom_wrapper)); - holder.binding.itemViewHolder.setOnClickListener { - runBlocking { - if (onClickListener != null) { - onClickListener!!.onClick(position, matterInfo) + holder.binding.itemViewHolder.setOnClickListener { + if (matterInfo.isDeviceOnline){ + runBlocking { + if (onClickListener != null) { + onClickListener!!.onClick(position, matterInfo) + } } + }else{ + return@setOnClickListener } - } holder.binding.imgDelete.setOnClickListener { runBlocking { @@ -89,7 +88,6 @@ class MatterScannedResultAdapter( interface OnClickListener { suspend fun onClick(position: Int, model: MatterScannedResultModel) - // suspend fun simpleCallback(position: Int, model: MatterScannedResultModel) suspend fun onDelete(position: Int, model: MatterScannedResultModel) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/controller/GenericChipDeviceListener.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/controller/GenericChipDeviceListener.kt index 15cd1653..98506372 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/controller/GenericChipDeviceListener.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/controller/GenericChipDeviceListener.kt @@ -1,54 +1,57 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.controller import chip.devicecontroller.ChipDeviceController +import chip.devicecontroller.ICDDeviceInfo -open class GenericChipDeviceListener: ChipDeviceController.CompletionListener { +open class GenericChipDeviceListener : ChipDeviceController.CompletionListener { override fun onConnectDeviceComplete() { - // No op + } override fun onStatusUpdate(status: Int) { - // No op } - override fun onPairingComplete(errorCode: Int) { - // No op + override fun onPairingComplete(errorCode: Long) { } - override fun onPairingDeleted(errorCode: Int) { - // No op + override fun onPairingDeleted(errorCode: Long) { + } - override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { + override fun onCommissioningComplete(nodeId: Long, errorCode: Long) { } + override fun onReadCommissioningInfo( vendorId: Int, productId: Int, wifiEndpointId: Int, threadEndpointId: Int ) { - // No op } - override fun onCommissioningStatusUpdate(nodeId: Long, stage: String?, errorCode: Int) { - // No op + override fun onCommissioningStatusUpdate(nodeId: Long, stage: String?, errorCode: Long) { + } override fun onNotifyChipConnectionClosed() { - // No op } override fun onCloseBleComplete() { - // No op } - override fun onError(error: Throwable) { - // No op + override fun onError(error: Throwable?) { + } + + override fun onOpCSRGenerationComplete(csr: ByteArray?) { + } + + override fun onICDRegistrationInfoRequired() { } - override fun onOpCSRGenerationComplete(csr: ByteArray) { - // No op + override fun onICDRegistrationComplete(errorCode: Long, icdDeviceInfo: ICDDeviceInfo?) { } + + } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterConnectFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterConnectFragment.kt deleted file mode 100644 index e3e6172f..00000000 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterConnectFragment.kt +++ /dev/null @@ -1,725 +0,0 @@ -package com.siliconlabs.bledemo.features.demo.matter_demo.fragments - -import android.annotation.SuppressLint -import android.app.Activity -import android.app.AlertDialog -import android.bluetooth.BluetoothDevice -import android.bluetooth.BluetoothGatt -import android.content.DialogInterface -import android.content.Intent -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.os.Build -import android.os.Bundle -import android.os.Handler -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.annotation.RequiresApi -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentTransaction -import androidx.lifecycle.lifecycleScope -import chip.devicecontroller.ChipClusters -import chip.devicecontroller.ChipDeviceController -import chip.devicecontroller.ChipStructs -import chip.devicecontroller.NetworkCredentials -import com.siliconlabs.bledemo.R -import com.siliconlabs.bledemo.databinding.FragmentMatterConnectBinding -import com.siliconlabs.bledemo.features.demo.matter_demo.activities.MatterDemoActivity -import com.siliconlabs.bledemo.features.demo.matter_demo.controller.GenericChipDeviceListener -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.CONTACT_SENSOR_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.LIGHTNING_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.LOCK_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.OCCUPANCY_SENSOR_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.PLUG_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.TEMPERATURE_SENSOR_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.THERMOSTAT_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.WINDOW_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannerFragment.Companion.INPUT_NETWORK_THREAD_TYPE_SELECTED -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannerFragment.Companion.INPUT_NETWORK_WIFI_TYPE_SELECTED -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannerFragment.Companion.INPUT_WIFI_REQ_CODE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannerFragment.Companion.WIFI_INPUT_PASSWORD -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannerFragment.Companion.WIFI_INPUT_SSID -import com.siliconlabs.bledemo.features.demo.matter_demo.manager.BluetoothManager -import com.siliconlabs.bledemo.features.demo.matter_demo.model.CHIPDeviceInfo -import com.siliconlabs.bledemo.features.demo.matter_demo.model.MatterScannedResultModel -import com.siliconlabs.bledemo.features.demo.matter_demo.model.NetworkCredentialsParcelable -import com.siliconlabs.bledemo.features.demo.matter_demo.utils.ChipClient -import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomInputDialog -import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomProgressDialog -import com.siliconlabs.bledemo.features.demo.matter_demo.utils.DeviceIDUtil -import com.siliconlabs.bledemo.features.demo.matter_demo.utils.FragmentUtils -import com.siliconlabs.bledemo.features.demo.matter_demo.utils.MessageDialogFragment -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import timber.log.Timber - - -class MatterConnectFragment : Fragment() { - private val channelInfo: String = "15" - private val panId: String = "1234" - private var customProgressDialog: CustomProgressDialog? = null - - private lateinit var extentedPanId: String - private lateinit var masterKey: String - - private lateinit var binding: FragmentMatterConnectBinding - private lateinit var deviceController: ChipDeviceController - private lateinit var scope: CoroutineScope - private lateinit var deviceInfo: CHIPDeviceInfo - private var gatt: BluetoothGatt? = null - private lateinit var bluetoothManager: BluetoothManager - private lateinit var networkCredential: NetworkCredentialsParcelable - private lateinit var matterScanDevice: BluetoothDevice - private var deviceId: Long = 0; - - private var dialog: AlertDialog? = null - private var otbrEntryDialog: MatterOTBRInputDialogFragment? = null - private var wifiEntryDialog: MatterWifiInputDialogFragment? = null - private lateinit var typeNtw: String - - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - deviceController = ChipClient.getDeviceController(requireContext()) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - super.onCreateView(inflater, container, savedInstanceState) - scope = viewLifecycleOwner.lifecycleScope - if (requireArguments() != null) { - typeNtw = checkNotNull(requireArguments().getString(DIA_INPUT_NTW_TYPE)) - deviceInfo = checkNotNull(requireArguments().getParcelable(ARG_DEVICE_INFO)) - } - binding = FragmentMatterConnectBinding.inflate(inflater, container, false) - return binding.root - } - - - @RequiresApi(Build.VERSION_CODES.R) - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - (activity as MatterDemoActivity).showQRScanner() - - val str = requireContext().getString(R.string.matter_commissioning_device) - showMatterProgressDialog(str) - Handler().postDelayed(Runnable { - removeProgress() - displayInputWindowBasedOnProvisionType() - }, 1000) - } - - private fun removeProgress() { - if (customProgressDialog?.isShowing == true) { - customProgressDialog?.dismiss() - } - } - - private fun displayInputWindowBasedOnProvisionType() { - when (typeNtw) { - INPUT_NETWORK_WIFI_TYPE_SELECTED /* ProvisionNetworkType.WIFI */ -> { - if (wifiEntryDialog == null) { - val prev = - requireActivity().supportFragmentManager.findFragmentByTag( - DIALOG_WIFI_INPUT_TAG - ) - if (prev == null) { - wifiEntryDialog = MatterWifiInputDialogFragment.newInstance() - wifiEntryDialog!!.setTargetFragment(this, DIALOG_WIFI_FRAGMENT) - wifiEntryDialog!!.show( - requireActivity().supportFragmentManager, - DIALOG_WIFI_INPUT_TAG - ) - } - } - } - - INPUT_NETWORK_THREAD_TYPE_SELECTED /* ProvisionNetworkType.THREAD*/ -> { - if (otbrEntryDialog == null) { - val prev = - requireActivity().supportFragmentManager.findFragmentByTag( - DIALOG_THREAD_INPUT_TAG - ) - if (prev == null) { - otbrEntryDialog = MatterOTBRInputDialogFragment.newInstance() - - otbrEntryDialog!!.setTargetFragment(this, DIALOG_THREAD_FRAGMENT) - otbrEntryDialog!!.show( - requireActivity().supportFragmentManager, - DIALOG_THREAD_INPUT_TAG - ) - } - } - } - - else -> { - println("Unhandled....") - } - - } - } - - @RequiresApi(Build.VERSION_CODES.R) - private fun validateWiFiInput(wifiSSID: String, wifiPassword: String) { - networkCredential = NetworkCredentialsParcelable - .forWiFi( - NetworkCredentialsParcelable.WiFiCredentials( - wifiSSID.toString(), wifiPassword.toString() - ) - ) - // pairDeviceWithAddress() - startConnectingToDevice() - } - - @SuppressLint("NewApi") - private fun validateThreadInput(inputOTBR: String) { - if (inputOTBR.isNullOrBlank()) { - Toast.makeText(requireContext(), "input OTBR is empty", Toast.LENGTH_SHORT).show() - return - } - val operationalDataset = dataFromHexString(inputOTBR.trim()) - - networkCredential = NetworkCredentialsParcelable - .forThread(NetworkCredentialsParcelable.ThreadCredentials(operationalDataset)) - startConnectingToDevice() - } - - @RequiresApi(Build.VERSION_CODES.R) - private fun validateInputOld(input: String) { -// if (input.isNullOrBlank()) { -// Toast.makeText(requireContext(), "input OTBR is empty", Toast.LENGTH_SHORT).show() -// return -// } - - //-------------------------------------------------------- - //Old Setup Supported Code - extentedPanId = resources.getString(R.string.extPanID) - masterKey = resources.getString(R.string.matMasterKey) - if (channelInfo.isNullOrBlank()) { - Toast.makeText(requireContext(), "Channel is empty", Toast.LENGTH_SHORT).show() - return - } - - if (panId.isNullOrBlank()) { - Toast.makeText(requireContext(), "PAN ID is empty", Toast.LENGTH_SHORT).show() - return - } - - if (extentedPanId.isNullOrBlank()) { - Toast.makeText(requireContext(), "XPAN ID is empty", Toast.LENGTH_SHORT).show() - return - } - val extendedPanIDStr = extentedPanId.toString().filterNot { c -> c == ':' } - if (extendedPanIDStr.length != NUM_XPANID_BYTES * 2) { - Toast.makeText(requireContext(), "Extended PAN ID is invalid", Toast.LENGTH_SHORT) - .show() - return - } - - if (masterKey.isNullOrBlank()) { - Toast.makeText(requireContext(), "Master Key is empty", Toast.LENGTH_SHORT).show() - return - } - - val masterKeyStr = masterKey.toString().filterNot { c -> c == ':' } - if (masterKeyStr.length != NUM_MASTER_KEY_BYTES * 2) { - Toast.makeText(requireContext(), "Master key is invalid", Toast.LENGTH_SHORT).show() - return - } - val extPID = extendedPanIDStr.hexToByteArray() - val masKey = masterKeyStr.hexToByteArray() - - val operationalDataset = makeThreadOperationalDataset( - channelInfo.toInt(), - panId.toInt(16), - extPID, - masKey - ) - println( - "OperationDataSet ${operationalDataset.size}" - ) - //-------------------------------------------------------- - - // val operationalDataset = dataFromHexString(input.trim()) - networkCredential = NetworkCredentialsParcelable - .forThread(NetworkCredentialsParcelable.ThreadCredentials(operationalDataset)) - startConnectingToDevice() - } - - - private fun convertInputToByteArray(strInput: String): ByteArray { - return strInput.toByteArray() - } - - private fun makeThreadOperationalDataset( - channel: Int, - panId: Int, - xpanId: ByteArray, - masterKey: ByteArray - ): ByteArray { - - // channel - var dataset = byteArrayOf(TYPE_CHANNEL.toByte(), NUM_CHANNEL_BYTES.toByte()) - dataset += 0x00.toByte() // Channel Page 0. - dataset += (channel.shr(8) and 0xFF).toByte() - dataset += (channel and 0xFF).toByte() - - // PAN ID - dataset += TYPE_PANID.toByte() - dataset += NUM_PANID_BYTES.toByte() - dataset += (panId.shr(8) and 0xFF).toByte() - dataset += (panId and 0xFF).toByte() - - // Extended PAN ID - dataset += TYPE_XPANID.toByte() - dataset += NUM_XPANID_BYTES.toByte() - dataset += xpanId - - // Network Master Key - dataset += TYPE_MASTER_KEY.toByte() - dataset += NUM_MASTER_KEY_BYTES.toByte() - dataset += masterKey - Timber.tag(TAG).e("dataset " + dataset.decodeToString()) - - return dataset - } - - - private fun String.hexToByteArray(): ByteArray { - return chunked(2).map { byteStr -> byteStr.toUByte(16).toByte() }.toByteArray() - } - - - @RequiresApi(Build.VERSION_CODES.R) - @SuppressLint("MissingPermission") - private fun startConnectingToDevice() { - if (gatt != null) { - return - } - scope.launch { - - bluetoothManager = BluetoothManager() - val strId = R.string.rendezvous_over_ble_scanning_text - val devInfo = deviceInfo.discriminator.toString() - showMessage(strId, devInfo) - val device = bluetoothManager.getBluetoothDevice( - requireContext(), - deviceInfo.discriminator, deviceInfo.isShortDiscriminator - ) ?: kotlin.run { - requireActivity().supportFragmentManager.popBackStack() - requireActivity().supportFragmentManager.popBackStack() - - showMessage(R.string.rendezvous_over_ble_scanning_failed_text) - return@launch - } - matterScanDevice = device - Timber.tag(TAG).e("Type :" + device.type.toString()) - Timber.tag(TAG).e("UUID :" + device.uuids.toString()) - Timber.tag(TAG).e("Name :" + device.name.toString()) - Timber.tag(TAG).e("Address :" + device.address.toString()) - Timber.tag(TAG).e("Alias :" + device.alias.toString()) - showMatterProgressDialog(getString(R.string.device_commissioning_in_progress) + " " + device.name) - - - showMessage( - R.string.rendezvous_over_ble_connecting_text, - device.name ?: device.address.toString() - ) - removeAlert() - deviceId = DeviceIDUtil.getNextAvailableId(requireContext()) - gatt = bluetoothManager.connect(requireContext(), device) - deviceController.setCompletionListener(ConnectionCallback()) - val connId = bluetoothManager.connectionId - var network: NetworkCredentials? = null - - - val thread = networkCredential.threadCredentials - if (thread != null) { - network = - NetworkCredentials.forThread( - NetworkCredentials.ThreadCredentials(thread.operationalDataset) - ) - } - val wifi = networkCredential.wiFiCredentials - if (wifi != null) { - network = - NetworkCredentials.forWiFi( - NetworkCredentials.WiFiCredentials(wifi.ssid, wifi.password) - ) - } - - setAttestationDelegate() - - deviceController.pairDevice( - gatt, - connId, - deviceId, - deviceInfo.setupPinCode, - network - ) - DeviceIDUtil.setNextAvailableId(requireContext(), deviceId + 1) - } - } - - private fun showMatterProgressDialog(message: String) { - - customProgressDialog = CustomProgressDialog(requireContext()) - customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - customProgressDialog!!.setMessage(message) - customProgressDialog!!.show() - } - - private fun setAttestationDelegate() { - deviceController.setDeviceAttestationDelegate(DEVICE_ATTESTATION_FAILED_TIMEOUT) { devicePtr, _, errorCode -> - - Timber.tag(TAG).e( - "Device attestation errorCode: $errorCode, " + - "Look at 'src/credentials/attestation_verifier/DeviceAttestationVerifier.h' " + - "AttestationVerificationResult enum to understand the errors" - ) - val activity = requireActivity() - Timber.tag(TAG).e("setAttestationDelegate()--errorCode:" + errorCode) - if (errorCode == STATUS_PAIRING_SUCCESS) { - Timber.tag(TAG).e("setAttestationDelegate() In--errorCode:" + errorCode) - activity.runOnUiThread(Runnable { - deviceController.continueCommissioning(devicePtr, true) - }) - - return@setDeviceAttestationDelegate - } - activity.runOnUiThread(Runnable { - if (dialog != null && dialog?.isShowing == true) { - Timber.tag(TAG).e("Dialog is already showing...") - return@Runnable - } - dialog = AlertDialog.Builder(activity) - .setPositiveButton("Continue", - DialogInterface.OnClickListener { dialog, id -> - deviceController.continueCommissioning(devicePtr, true) - }) - .setNegativeButton("No", - DialogInterface.OnClickListener { dialog, id -> - deviceController.continueCommissioning(devicePtr, false) - }) - .setTitle("Device Attestation") - .setMessage("Device Attestation failed for device under commissioning. Do you wish to continue pairing?") - .show() - }) - - } - } - - private fun removeAlert() { - if (otbrEntryDialog != null) { - otbrEntryDialog!!.dismiss() - } - } - - private fun onCommissionCompleted() { - ChipClient.getDeviceController(requireContext()).close() - } - - inner class ConnectionCallback : GenericChipDeviceListener() { - override fun onConnectDeviceComplete() { - super.onConnectDeviceComplete() - Timber.tag(TAG).e("onConnectDeviceComplete") - } - - override fun onStatusUpdate(status: Int) { - super.onStatusUpdate(status) - Timber.tag(TAG).e("onStatusUpdate : $status.toString()") - } - - - @SuppressLint("MissingPermission") - override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { - super.onCommissioningComplete(nodeId, errorCode) - Timber.tag(TAG).e("onCommissioningComplete : NodeID: $nodeId.toString()") - Timber.tag(TAG).e("onCommissioningComplete : errorCode: " + errorCode.toString()) - removeAlert() - if (errorCode == STATUS_PAIRING_SUCCESS) { - if (customProgressDialog?.isShowing == true) { - customProgressDialog?.dismiss() - } - Timber.tag(TAG).e("pairing success") - onCommissionCompleted() - - scope.launch { - getDescriptorClusterForDevice().readDeviceTypeListAttribute(object : - ChipClusters.DescriptorCluster.DeviceTypeListAttributeCallback { - override fun onSuccess(valueList: MutableList?) { - - Timber.tag(TAG) - .e("deviceType $ valueList?.get(0)?.deviceType?.toInt()!!") - val deviceType = valueList?.get(0)?.deviceType?.toInt()!! - println("device Type: $deviceType}") - println("device Info: ${matterScanDevice.name} DeviceId: ${deviceId}") - var device: String = "" - - when (deviceType) { - LOCK_TYPE -> - device = - requireContext().getString(R.string.matter_lock_list) - - PLUG_TYPE -> - device = - requireContext().getString(R.string.matter_plug_list) - - OCCUPANCY_SENSOR_TYPE -> - device = - requireContext().getString(R.string.matter_occupancy_sensor_list) - - TEMPERATURE_SENSOR_TYPE -> - device = - requireContext().getString(R.string.matter_temperature_sensor_list) - - CONTACT_SENSOR_TYPE -> - device = - requireContext().getString(R.string.matter_contact_sensor_list) - - THERMOSTAT_TYPE -> - device = - requireContext().getString(R.string.matter_thermostat_list) - - LIGHTNING_TYPE -> - device = - requireContext().getString(R.string.matter_light_list) - - WINDOW_TYPE -> - device = - requireContext().getString(R.string.matter_window_list) - - else -> device = matterScanDevice.name - - - // else -> device = matterScanDevice.name - } - - val deviceName = device + COLON_WITH_SPACE + deviceId - println("device Info: ${deviceName} DeviceId: ${deviceId}") - - ShowEditDeviceNameDialog(device, valueList) - - - } - - private fun ShowEditDeviceNameDialog( - device: String, - valueList: MutableList - ) { - - val customInputDialog = CustomInputDialog.newInstance( - requireContext(), device, getString(R.string.add_device_name), - getString( - R.string.add_device_subtitle - ) - ) - - - customInputDialog.setOnButtonClickListener { deviceName -> - - val matterInfo = MatterScannedResultModel( - deviceName, - matterScanDevice.address, - matterScanDevice.type, - deviceId, - valueList?.get(0)?.deviceType?.toInt()!!, - isDeviceOnline = true - ) - FragmentUtils.getHost( - this@MatterConnectFragment, - Callback:: - class.java - ) - .onCommissionCompleteLoadData(matterInfo) - - } - - customInputDialog.show(requireFragmentManager(), "CustomInputDialog") - - - } - - override fun onError(ex: Exception?) { - Timber.tag(TAG).e("failed to read readDeviceTypeListAttribute" + ex) - } - }) - } - - - } else { - if (customProgressDialog?.isShowing() == true) { - customProgressDialog?.dismiss() - } - - /*val strId = R.string.matter_device_offline_text - showMessages(strId)*/ - } - onCommissionCompleted() - } - - override fun onPairingComplete(errorCode: Int) { - super.onPairingComplete(errorCode) - Timber.tag(TAG).e("onPairingComplete: $errorCode") - if (errorCode != STATUS_PAIRING_SUCCESS) { - showMessage(R.string.rendezvous_over_ble_pairing_failure_text) - onCommissionCompleted() - } - } - - override fun onOpCSRGenerationComplete(csr: ByteArray) { - super.onOpCSRGenerationComplete(csr) - Timber.tag(TAG).e(String(csr)) - } - - override fun onPairingDeleted(errorCode: Int) { - super.onPairingDeleted(errorCode) - Timber.tag(TAG).e("onPairingDeleted: $errorCode") - } - - override fun onCloseBleComplete() { - super.onCloseBleComplete() - Timber.tag(TAG).e("onCloseBleComplete") - } - - override fun onError(error: Throwable) { - super.onError(error) - Timber.tag(TAG).d("onError: $error") - } - } - - - private fun showMessages(msgId: Int) { - requireActivity().runOnUiThread { - val resString = requireContext().getString(msgId) - Toast.makeText(requireContext(), resString, Toast.LENGTH_SHORT).show() - } - } - - private fun showMessage(msgResId: Int, stringArgs: String? = null) { - requireActivity().runOnUiThread { - val context = requireContext() - val msg = context.getString(msgResId, stringArgs) - Timber.tag(TAG).e("showMessage:$msg") - Toast.makeText(context, msg, Toast.LENGTH_SHORT) - .show() - } - } - - @RequiresApi(Build.VERSION_CODES.R) - override fun onActivityResult(requestCode: Int, resultCode: Int, dataReceived: Intent?) { - super.onActivityResult(requestCode, resultCode, dataReceived) - if (resultCode == Activity.RESULT_OK) { - if (dataReceived != null) { - val receiveOTBRInfo = dataReceived.getStringExtra(DIALOG_OTBR_INFO) - - println("OTBR data: $receiveOTBRInfo") - removeAlert() - validateThreadInput(receiveOTBRInfo!!.trim()) - } - } else if (resultCode == INPUT_WIFI_REQ_CODE) { - if (dataReceived != null) { - val wifiSSID = dataReceived.getStringExtra(WIFI_INPUT_SSID) - val wifiPassword = dataReceived.getStringExtra(WIFI_INPUT_PASSWORD) - println("wifiSSID: ${wifiSSID} === wifiPassword:${wifiPassword}") - if (wifiSSID != null && wifiPassword != null) { - validateWiFiInput(wifiSSID, wifiPassword) - } - } - } - } - - override fun onDestroy() { - super.onDestroy() - deviceController.close() - } - - - interface Callback { - fun onCommissionCompleteLoadData(matterScannedResultModel: MatterScannedResultModel) - } - - private suspend fun getDescriptorClusterForDevice(): ChipClusters.DescriptorCluster { - return ChipClusters.DescriptorCluster( - - ChipClient.getConnectedDevicePointer(requireContext(), deviceId), - ON_OFF_CLUSTER_ENDPOINT - ) - } - - - private fun dataFromHexString(string: String): ByteArray { - var modifiedString = string - if (string.length % 2 == 1) { - modifiedString = "0$string" - } - - val chars = modifiedString.toCharArray() - var i = 0 - val len = modifiedString.length - - - val data = ByteArray(len / 2) - val byteChars = CharArray(2) - - - while (i < len) { - byteChars[0] = chars[i++] - byteChars[1] = chars[i++] - var wholeByte = 0L - try { - wholeByte = byteChars.joinToString("").toLong(16) - - } catch (ex: NumberFormatException) { - var wholeByte = 0L - Timber.tag(TAG).e("Number Format Exception Occurred..") - } - - data[(i - 2) / 2] = wholeByte.toByte() - } - - return data - } - - - companion object { - private val TAG = "MatterConnectFragment" - private const val ARG_DEVICE_INFO = "device_info" - private const val NUM_CHANNEL_BYTES = 3 - private const val NUM_PANID_BYTES = 2 - private const val NUM_XPANID_BYTES = 8 - private const val NUM_MASTER_KEY_BYTES = 16 - private const val TYPE_CHANNEL = 0 // Type of Thread Channel TLV. - private const val TYPE_PANID = 1 // Type of Thread PAN ID TLV. - private const val TYPE_XPANID = 2 // Type of Thread Extended PAN ID TLV. - private const val TYPE_MASTER_KEY = 5 // Type of Thread Network Master Key TLV. - public const val MATTER_DEVICE_LOCK = "MATTER-3840" - public const val MATTER_DEVICE_LIGHT = "Silabs-Light" - public const val MATTER_DEVICE_WINDOW = "Silabs-Window" - public const val MATTER_DEVICE_SENSOR = "SL-SENSOR" - private const val STATUS_PAIRING_SUCCESS = 0 - private const val DEVICE_ATTESTATION_FAILED_TIMEOUT = 600 - public const val ON_OFF_CLUSTER_ENDPOINT = 1 - private const val COLON_WITH_SPACE = " - " - public const val SPACE = " " - private const val DIALOG_THREAD_FRAGMENT = 999 - private const val DIALOG_WIFI_FRAGMENT = 998 - const val DIA_INPUT_NTW_TYPE = "dia_input_ntw_type" - const val DIALOG_OTBR_INFO = "Dialog_OTBR_Info" - private const val DIALOG_THREAD_INPUT_TAG = "MatterThreadDialogFragmentTAG" - private const val DIALOG_WIFI_INPUT_TAG = "MatterWiFiDialogFragmentTAG" - fun newInstance(): MatterConnectFragment { - val args = Bundle() - - val fragment = MatterConnectFragment() - fragment.arguments = args - return fragment - } - } - -} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterContactSensorFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterContactSensorFragment.kt index bbd51868..42b9bd4a 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterContactSensorFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterContactSensorFragment.kt @@ -1,11 +1,9 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.fragments -import android.content.Context import android.content.SharedPreferences import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -34,15 +32,12 @@ import com.siliconlabs.bledemo.features.demo.matter_demo.utils.MessageDialogFrag import com.siliconlabs.bledemo.features.demo.matter_demo.utils.SharedPrefsUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.MainScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.jetbrains.annotations.NotNull import timber.log.Timber -import kotlin.Exception class MatterContactSensorFragment : Fragment() { @@ -64,29 +59,25 @@ class MatterContactSensorFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mPrefs = requireContext().getSharedPreferences( - "your_preference_name", + MatterDemoActivity.MATTER_PREF, AppCompatActivity.MODE_PRIVATE ) if (requireArguments() != null) { model = requireArguments().getParcelable(ARG_DEVICE_MODEL)!! deviceId = model.deviceId - Timber.tag(TAG).e( "deviceID: " + model) + Timber.tag(TAG).e("deviceID: $model") } if (deviceId != null) { + showMatterProgressDialog(getString(R.string.matter_device_status)) - showMatterProgressDialog(getString(R.string.please_wait)) - - // retrieveSavedDevices() - GlobalScope.launch { + CoroutineScope(Dispatchers.IO).launch { // This code will run asynchronously - val resultq = checkForDeviceStatus() - if (resultq) { + val resultInfo = checkForDeviceStatus() + if (resultInfo) { println("Operation was successful") removeProgress() - // prepareList() - } } } @@ -96,7 +87,7 @@ class MatterContactSensorFragment : Fragment() { } private fun removeProgress() { - if (customProgressDialog?.isShowing() == true) { + if (customProgressDialog?.isShowing == true) { customProgressDialog?.dismiss() } } @@ -110,7 +101,7 @@ class MatterContactSensorFragment : Fragment() { override fun onDeviceConnected(devicePointer: Long) { model.isDeviceOnline = true SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) - removeProgress() + removeProgress() } @@ -136,13 +127,14 @@ class MatterContactSensorFragment : Fragment() { customProgressDialog = CustomProgressDialog(requireContext()) customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) customProgressDialog!!.setMessage(message) + customProgressDialog!!.setCanceledOnTouchOutside(false) customProgressDialog!!.show() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { binding = FragmentMatterContactSensorBinding.inflate(inflater, container, false) return binding.root @@ -159,19 +151,17 @@ class MatterContactSensorFragment : Fragment() { } - fun startUpdates() { + private fun startUpdates() { stopUpdates() job = scopeFun.launch { while (true) { - // getData() // the function that should be ran every second - // sendReadValueCommandClick() sendReadValue() delay(2000) } } } - fun stopUpdates() { + private fun stopUpdates() { job?.cancel() job = null } @@ -204,7 +194,6 @@ class MatterContactSensorFragment : Fragment() { getContactSensorClusterForDevice().readStateValueAttribute( object : ChipClusters.BooleanAttributeCallback { override fun onSuccess(value: Boolean) { - // removeProgress() println("Contact Value : $value") SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) requireActivity().runOnUiThread { @@ -219,11 +208,10 @@ class MatterContactSensorFragment : Fragment() { } override fun onError(error: Exception?) { - // removeProgress() SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) showMessageDialog() - Timber.tag(TAG).e( "error readStateValueAttribute " + error) + Timber.tag(TAG).e("error readStateValueAttribute :$error") } } @@ -237,24 +225,17 @@ class MatterContactSensorFragment : Fragment() { } } - override fun onAttach(@NotNull context: Context) { - super.onAttach(context) - } - - override fun onDetach() { - super.onDetach() - } private fun showMessageDialog() { try { - if (isAdded() && requireActivity() != null && !requireActivity().isFinishing) { + if (isAdded && !requireActivity().isFinishing) { requireActivity().runOnUiThread { if (!MessageDialogFragment.isDialogShowing()) { dialog = MessageDialogFragment() dialog.setMessage(getString(R.string.matter_device_offline_text)) dialog.setOnDismissListener { removeProgress() - if (requireActivity().supportFragmentManager.getBackStackEntryCount() > 0) { - requireActivity().supportFragmentManager.popBackStack(); + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + requireActivity().supportFragmentManager.popBackStack() } else { FragmentUtils.getHost( this@MatterContactSensorFragment, @@ -272,33 +253,32 @@ class MatterContactSensorFragment : Fragment() { } else { Timber.e(TAG, "device offline") } - }catch (e:Exception){ - Timber.e(TAG,""+ e) + } catch (e: Exception) { + Timber.e(TAG, "Exception Occurred: $e") } - } inner class ContactSensorChipControllerCallback : GenericChipDeviceListener() { override fun onConnectDeviceComplete() {} - override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { - Timber.tag(TAG).d( "onCommissioningComplete for nodeId $nodeId: $errorCode") - // showMessage("Address update complete for nodeId $nodeId with code $errorCode") + override fun onCommissioningComplete(nodeId: Long, errorCode: Long) { + Timber.tag(TAG).d("onCommissioningComplete for nodeId $nodeId: $errorCode") + // showMessage("Address update complete for nodeId $nodeId with code $errorCode") } override fun onNotifyChipConnectionClosed() { - Timber.tag(TAG).d( "onNotifyChipConnectionClosed") + Timber.tag(TAG).d("onNotifyChipConnectionClosed") } override fun onCloseBleComplete() { - Timber.tag(TAG).d( "onCloseBleComplete") + Timber.tag(TAG).d("onCloseBleComplete") } - override fun onError(error: Throwable) { + override fun onError(error: Throwable?) { super.onError(error) - Timber.tag(TAG).d( "onError : $error") + Timber.tag(TAG).d("onError : $error") } } @@ -307,7 +287,7 @@ class MatterContactSensorFragment : Fragment() { } companion object { - private val TAG = MatterContactSensorFragment.javaClass.simpleName.toString() + private val TAG = Companion::class.java.simpleName.toString() fun newInstance(): MatterContactSensorFragment = MatterContactSensorFragment() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterDishwasherFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterDishwasherFragment.kt new file mode 100644 index 00000000..9851a85b --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterDishwasherFragment.kt @@ -0,0 +1,1238 @@ +package com.siliconlabs.bledemo.features.demo.matter_demo.fragments + +import android.annotation.SuppressLint +import android.content.SharedPreferences +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.os.CountDownTimer +import android.text.Html +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.activity.OnBackPressedCallback +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.text.HtmlCompat +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentTransaction +import androidx.lifecycle.lifecycleScope +import chip.devicecontroller.ChipClusters +import chip.devicecontroller.ChipClusters.ElectricalEnergyMeasurementCluster +import chip.devicecontroller.ChipClusters.OperationalStateCluster +import chip.devicecontroller.ChipClusters.DeviceEnergyManagementCluster +import chip.devicecontroller.ChipDeviceController +import chip.devicecontroller.ChipStructs +import chip.devicecontroller.GetConnectedDeviceCallbackJni +import com.google.gson.Gson +import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.bluetooth.beacon_utils.eddystone.Constants.SCAN_TIMER +import com.siliconlabs.bledemo.databinding.FragmentMatterDishwasherBinding +import com.siliconlabs.bledemo.features.demo.matter_demo.activities.MatterDemoActivity +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.CallBackHandler +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ARG_DEVICE_MODEL +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.INIT +import com.siliconlabs.bledemo.features.demo.matter_demo.model.DishWasherModel +import com.siliconlabs.bledemo.features.demo.matter_demo.model.MatterScannedResultModel +import com.siliconlabs.bledemo.features.demo.matter_demo.utils.ChipClient +import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomProgressDialog +import com.siliconlabs.bledemo.features.demo.matter_demo.utils.FragmentUtils +import com.siliconlabs.bledemo.features.demo.matter_demo.utils.MessageDialogFragment +import com.siliconlabs.bledemo.features.demo.matter_demo.utils.SharedPrefsUtils +import com.siliconlabs.bledemo.features.demo.matter_demo.utils.SharedPrefsUtils.ARG_ADDED_DEVICE_INFO_LIST +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import timber.log.Timber + +class MatterDishwasherFragment : Fragment() { + private var progress: Int = 0 + private lateinit var dialog: MessageDialogFragment + private val dialogTag = "MessageDialog" + private val deviceController: ChipDeviceController + get() = ChipClient.getDeviceController(requireContext()) + private lateinit var mPrefs: SharedPreferences + + private lateinit var scope: CoroutineScope + lateinit var binding: FragmentMatterDishwasherBinding + private var deviceId: Long = INIT + private var endpointId: Int = DISHWASHER_OPT_ENDPOINT + private lateinit var model: MatterScannedResultModel + private var customProgressDialog: CustomProgressDialog? = null + private var timer: CountDownTimer? = null + private var timeLeftInMillis: Long = 600000 // 10 minutes in milliseconds + private var isTimerRunning = false + private var isPaused = false + private var currentTotalEnergy: Float = 0.000F + private var currentInCycleEnergy: Float = 0.000F + private var cycleCounter: Int? = 0 + private var mCurrentCycleState: Boolean = true + var mInCurrentCycleGlobalState = 0.000F + private lateinit var dishwasherPref: SharedPreferences + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mPrefs = requireContext().getSharedPreferences( + MatterDemoActivity.MATTER_PREF, AppCompatActivity.MODE_PRIVATE + ) + dishwasherPref = requireContext().getSharedPreferences( + DISHWASHER_PREF, AppCompatActivity.MODE_PRIVATE + ) + model = requireArguments().getParcelable(ARG_DEVICE_MODEL)!! + deviceId = model.deviceId + Timber.tag(TAG).e("deviceID: $model") + + showMatterProgressDialog(getString(R.string.matter_device_status)) + CoroutineScope(Dispatchers.IO).launch { + val resultInfo = checkForDeviceStatus() + withContext(Dispatchers.Main) { + if (resultInfo) { + println("Operation was successful") + removeProgress() + } + } + } + } + + private suspend fun checkForDeviceStatus(): Boolean { + return withContext(Dispatchers.Default) { + // Simulate a time-consuming task + + deviceController.getConnectedDevicePointer( + deviceId, + object : GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback { + override fun onDeviceConnected(devicePointer: Long) { + println("----DevicePointer:$devicePointer") + model.isDeviceOnline = true + SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) + } + + override fun onConnectionFailure(nodeId: Long, error: java.lang.Exception?) { + model.isDeviceOnline = false + removeProgress() + println("----nodeId:$nodeId") + println("----error:${error!!.message}") + SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) + showMessageDialog() + + } + }) + + delay(SCAN_TIMER * 1000) + true + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + super.onCreateView(inflater, container, savedInstanceState) + binding = FragmentMatterDishwasherBinding.inflate(inflater, container, false) + viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { + binding.btnOn.isEnabled = true + binding.btnOff.isEnabled = false + binding.btnPause.isEnabled = false + requireActivity().runOnUiThread { + imageDishwasherOff() + } + } + return binding.root + } + + private fun showMessageDialog() { + + try { + if (isAdded && !requireActivity().isFinishing) { + requireActivity().runOnUiThread { + if (!MessageDialogFragment.isDialogShowing()) { + dialog = MessageDialogFragment() + dialog.setMessage(getString(R.string.matter_device_offline_text)) + dialog.setOnDismissListener { + removeProgress() + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + requireActivity().supportFragmentManager.popBackStack() + } else { + FragmentUtils.getHost( + this@MatterDishwasherFragment, CallBackHandler::class.java + ).onBackHandler() + } + } + val transaction: FragmentTransaction = + requireActivity().supportFragmentManager.beginTransaction() + + dialog.show(transaction, dialogTag) + } + } + } else { + Timber.e("device offline") + } + } catch (e: Exception) { + Timber.tag(TAG).e("device offline device offline ${e.message}") + } + + } + + private fun removeProgress() { + if (customProgressDialog?.isShowing == true) { + customProgressDialog?.dismiss() + } + } + + private fun showMatterProgressDialog(message: String) { + customProgressDialog = CustomProgressDialog(requireContext()) + customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + customProgressDialog!!.setMessage(message) + customProgressDialog!!.setCanceledOnTouchOutside(false) + customProgressDialog!!.show() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + scope = viewLifecycleOwner.lifecycleScope + updateCompletedCycleTextCounter() + binding.btnOn.text = requireContext().getString(R.string.matter_dishwasher_on) + binding.btnOff.text = requireContext().getString(R.string.matter_dishwasher_off) + binding.btnPause.text = requireContext().getString(R.string.matter_dishwasher_pause) + (activity as MatterDemoActivity).hideQRScanner() + + binding.btnOn.setOnClickListener { + DishWasherModel.dishwasherCurrentRunningState = "Started" + SharedPrefsUtils.saveDishwasherAppliedCycleStates(dishwasherPref, "Started") + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${currentInCycleEnergy}${" kWh"} " + + ) + binding.btnOff.isEnabled = true + binding.btnPause.isEnabled = true + binding.btnOn.isEnabled = false + scope.launch { + turnOnDishwasher() + readEnergy() + startDishwasherTimer() + } + } + binding.btnOff.setOnClickListener { + currentInCycleEnergy = 0.000F + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${ + 0.000f + }${" kWh"} " + + ) + + SharedPrefsUtils.saveDishwasherInCurrentCycleEnergyConsumed(dishwasherPref, 0.000f) + DishWasherModel.dishwasherCurrentRunningState = "Stopped" + SharedPrefsUtils.saveDishwasherAppliedCycleStates(dishwasherPref, "Stopped") + cycleCounter = cycleCounter!! + 1 + updateCompletedCycleTextCounter() + binding.btnOn.isEnabled = true + binding.btnOff.isEnabled = false + binding.btnPause.isEnabled = false + mCurrentCycleState = false + scope.launch { + //getEleDevMag().subscribeCumulativeEnergyImportedAttribute(null,0,0) + //forcefully read the Dishwasher Firmware again! + readEnergy() + turnOffDishwasher() + stopDishwasherTimer() + } + mInCurrentCycleGlobalState = 0.000F + cycleCounter?.let { + SharedPrefsUtils.saveDishwasherCompletedCycleCount( + dishwasherPref, it + ) + } + } + binding.btnPause.setOnClickListener { + binding.btnOn.isEnabled = false + binding.btnOff.isEnabled = true + binding.btnPause.isEnabled = true + if (isPaused) { + //resume dishwasher operations + scope.launch { + resumeDishwasher() + resumeDishwasherTimer() + } + + } else { + //pause dishwasher operations + scope.launch { + pauseDishwasher() + pauseDishwasherTimer() + } + + } + isPaused = !isPaused + if (isPaused) { + DishWasherModel.dishwasherCurrentRunningState = "Paused" + SharedPrefsUtils.saveDishwasherAppliedCycleStates(dishwasherPref, "Paused") + } else { + DishWasherModel.dishwasherCurrentRunningState = "Resumed" + SharedPrefsUtils.saveDishwasherAppliedCycleStates(dishwasherPref, "Resumed") + } + } + updateCountDownTimerText() + binding.matterProgressBar.max = 100 + binding.matterProgressBar.rotation = 180F + //back handling + requireActivity().onBackPressedDispatcher.addCallback( + requireActivity(), onBackPressedCallback + ) + } + + + private fun showAlertDialogForOtherOperations() { + val dialog = AlertDialog.Builder(requireContext()).setTitle(getString(R.string.alert)) + .setMessage(getString(R.string.matter_dishwasher_navigating_back)) + .setPositiveButton("OK") { _, _ -> + // Explicitly pause and navigate back + binding.btnPause.performClick() + DishWasherModel.dishwasherCurrentRunningState = "Paused" + SharedPrefsUtils.saveDishwasherAppliedCycleStates(dishwasherPref, "Paused") + requireActivity().supportFragmentManager.popBackStack() + }.setNegativeButton("Cancel") { dialog, _ -> + dialog.dismiss() + }.create() + + dialog.show() + } + + private val onBackPressedCallback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (isAdded) { + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + handleDishwasherBackNavigationUI() + } else { + FragmentUtils.getHost( + this@MatterDishwasherFragment, + MatterLightFragment.CallBackHandler::class.java + ).onBackHandler() + } + } + } + } + + fun handleDishwasherBackNavigationUI() { + IS_BACK_BUTTON_CLICKED = true + if (DishWasherModel.dishwasherCurrentRunningState == "Started" || + DishWasherModel.dishwasherCurrentRunningState == "Resumed") { + showAlertDialogForOtherOperations() + } else { + requireActivity().supportFragmentManager.popBackStack() + } + } + + fun showAlertDialogForPausedState() { + val dialog = AlertDialog.Builder(requireContext()).setTitle(getString(R.string.alert)) + .setMessage(getString(R.string.matter_dishwasher_navigating_back)) + .setPositiveButton("OK") { _, _ -> + // Navigate back without explicitly pausing + DishWasherModel.dishwasherCurrentRunningState = "Paused" + SharedPrefsUtils.saveDishwasherAppliedCycleStates(dishwasherPref, "Paused") + requireActivity().supportFragmentManager.popBackStack() + }.setNegativeButton("Cancel") { dialog, _ -> + dialog.dismiss() + }.create() + + dialog.show() + } + + + private fun updateCompletedCycleTextCounter() { + val htmlString = + "${context?.getString(R.string.matter_completed_cycle)}${" "}$cycleCounter" + // Set the HTML string to the TextView + binding.tvCompletedCycleText.text = Html.fromHtml(htmlString, Html.FROM_HTML_MODE_LEGACY) + } + + private fun resumeDishwasherTimer() { + isTimerRunning = true + timer = object : CountDownTimer(timeLeftInMillis, 1000) { + override fun onTick(millisUntilFinished: Long) { + timeLeftInMillis = millisUntilFinished + updateCountDownTimerText() + updateMatterProgressBar() + SharedPrefsUtils.saveDishwasherTimeLeftFormatted(dishwasherPref, timeLeftInMillis) + } + + override fun onFinish() { + updateCompletedCycleTextCounter() + binding.btnOff.performClick() + SharedPrefsUtils.saveDishwasherTimeLeftFormatted(dishwasherPref, timeLeftInMillis) + } + }.start() + //isPaused = false + binding.btnPause.text = getString(R.string.matter_dishwasher_pause) + + } + + private suspend fun resumeDishwasher() { + getDishwasherClusterForDevice().resume(object : + OperationalStateCluster.OperationalCommandResponseCallback { + override fun onError(error: java.lang.Exception?) { + println("Error: ${error!!.message}") + } + + override fun onSuccess(commandResponseState: ChipStructs.OperationalStateClusterErrorStateStruct?) { + if (isAdded) { + requireActivity().runOnUiThread { + imageDishwasherResume() + if (SharedPrefsUtils.getDishwasherAppliedCycleStates(dishwasherPref) == "Resumed") { + if (SharedPrefsUtils.getDishwasherCompletedCycleCount(dishwasherPref) <= 0) { + currentTotalEnergy = + SharedPrefsUtils.getDishwasherInCurrentCycleEnergyConsumed( + dishwasherPref + ) + } else { + /*currentInCycleEnergy = + SharedPrefsUtils.getDishwasherInCurrentCycleEnergyConsumed( + dishwasherPref + ) + println("ScccccjustDishwasherInResume $currentInCycleEnergy")*/ + currentTotalEnergy = + SharedPrefsUtils.getDishwasherTotalEnergyConsumption( + dishwasherPref + ) + + } + scope.launch { + if (isAdded) { + scope.launch(Dispatchers.Main) { + progress = + SharedPrefsUtils.getDishwasherCompletedCycleProgressBar( + dishwasherPref + ) + updateMatterProgressBar() + } + } + + readEnergyWhenInPauseOrResumeState() + + } + } + + } + } + + } + }, TIME_OUT) + } + + private suspend fun readEnergyWhenInPauseOrResumeState() { + getEleDevMag().subscribeCumulativeEnergyImportedAttribute(object : + ElectricalEnergyMeasurementCluster.CumulativeEnergyImportedAttributeCallback { + override fun onError(error: java.lang.Exception?) { + if (error != null) { + println("Error :${error.message}") + Timber.tag(TAG).e("Error :${error.message}") + if (isAdded && !requireActivity().isFinishing) { + requireActivity().runOnUiThread { + Toast.makeText( + requireContext(), + "Error Occurred:${error.message}", + Toast.LENGTH_SHORT + ).show() + } + } + + + } + } + + @SuppressLint("SetTextI18n") + override fun onSuccess(value: ChipStructs.ElectricalEnergyMeasurementClusterEnergyMeasurementStruct?) { + if (isAdded && !requireActivity().isFinishing) { + requireActivity().runOnUiThread { + val energyValue = value?.energy + if (null != energyValue) { + val str = (energyValue?.toDouble())?.div(1_000_000) + val formattedEnergyValue = String.format("%.3f", str) + if (formattedEnergyValue.isNotEmpty() || formattedEnergyValue.isNotBlank()) { + + //currentTotalEnergy = formattedEnergyValue.toFloat() + var previousCycle = + SharedPrefsUtils.getDishwasherInCurrentCycleEnergyConsumed( + dishwasherPref + ) + currentTotalEnergy = + SharedPrefsUtils.getDishwasherTotalEnergyConsumption( + dishwasherPref + ) + currentInCycleEnergy = (formattedEnergyValue.toFloat() - currentTotalEnergy)+previousCycle + + //in current cycle + if (SharedPrefsUtils.getDishwasherCompletedCycleCount(dishwasherPref) <= 0) { + // when the completed cycle is zero that is during the first cycle, in current cycle + //will be the Total energy coming from the firmware + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${ + String.format( + "%.3f", currentTotalEnergy + ) + }${" kWh"} " + + ) + lifecycleScope.launch(Dispatchers.IO) { + SharedPrefsUtils.saveDishwasherInCurrentCycleEnergyConsumed( + dishwasherPref, + String.format("%.3f", currentTotalEnergy).toFloat() + ) + } + + } else { + //lifecycleScope.launch { + //delay(60000) + if(!DishWasherModel.dishwasherCurrentRunningState.equals( + "Stopped", ignoreCase = true + )){ + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${ + String.format( + "%.3f", currentInCycleEnergy + ) + }${" kWh"} " + + ) + // } + + lifecycleScope.launch(Dispatchers.IO) { + SharedPrefsUtils.saveDishwasherInCurrentCycleEnergyConsumed( + dishwasherPref, + String.format("%.3f", currentInCycleEnergy).toFloat() + ) + } + } + + if(DishWasherModel.dishwasherCurrentRunningState.equals( + "Stopped", ignoreCase = true + )) { + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + } + } + // in Total + if (cycleCounter!! <= 0) { + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "$formattedEnergyValue${" kWh"} " + + ) + + lifecycleScope.launch(Dispatchers.IO) { + SharedPrefsUtils.saveDishwasherTotalEnergyConsumption( + dishwasherPref, formattedEnergyValue.toFloat() + ) + } + } else { + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "$formattedEnergyValue${" kWh"} " + + ) + + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${ + String.format( + "%.3f", formattedEnergyValue.toFloat().div(cycleCounter!!) + ) + }${" kWh"} " + + ) + + lifecycleScope.launch(Dispatchers.IO) { + SharedPrefsUtils.saveDishwasherTotalEnergyConsumption( + dishwasherPref, formattedEnergyValue.toFloat() + ) + } + } + + // average energy per cycle + val averageCurrentEnergyPerCycle = + formattedEnergyValue.toFloat().div(cycleCounter!!) + if (averageCurrentEnergyPerCycle.isInfinite()) { + + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + } + if (cycleCounter!! <= 0) { + + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${formattedEnergyValue}${" kWh"} " + + ) + + lifecycleScope.launch(Dispatchers.IO) { + SharedPrefsUtils.saveDishwasherAverageEnergyPerCycle( + dishwasherPref, formattedEnergyValue.toFloat() + ) + } + } else { + /*if (DishWasherModel.dishwasherCurrentRunningState.equals( + "Stopped", ignoreCase = true + ) + ) {*/ + + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${ + String.format( + "%.3f", averageCurrentEnergyPerCycle + ) + }${" kWh"} " + + ) + + lifecycleScope.launch(Dispatchers.IO) { + SharedPrefsUtils.saveDishwasherAverageEnergyPerCycle( + dishwasherPref, + String.format("%.3f", averageCurrentEnergyPerCycle) + .toFloat() + ) + } + //} + } + } else { + + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + } + } else { + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + } + + } + } + + } + }, 1, 2) + } + + + private fun imageDishwasherResume() { + if (isAdded) { + requireActivity().runOnUiThread { + binding.btnMatterDeviceState.setImageResource(R.drawable.dishwasher_state_on) + binding.btnMatterDeviceState.setColorFilter( + ContextCompat.getColor(requireContext(), R.color.silabs_dark_blue), + android.graphics.PorterDuff.Mode.SRC_IN + ) + binding.txtClusterStatus.text = requireContext().getString(R.string.blinky_tb_on) + } + } + } + + private fun updateCountDownTimerText() { + val minutes = (timeLeftInMillis / 1000) / 60 + val seconds = (timeLeftInMillis / 1000) % 60 + + val timeLeftFormatted = String.format("%02d:%02d", minutes, seconds) + val htmlString = + "${context?.getString(R.string.matter_remaining_time)}${" "}$timeLeftFormatted ${ + context?.getString(R.string.matter_min) + }" + // Set the HTML string to the TextView + binding.progressMatterText.text = Html.fromHtml(htmlString, Html.FROM_HTML_MODE_LEGACY) + + } + + private fun pauseDishwasherTimer() { + if (!isTimerRunning) return + + timer?.cancel() + isTimerRunning = false + //isPaused = true + binding.btnPause.text = getString(R.string.matter_resume) + } + + private fun stopDishwasherTimer() { + timer?.cancel() + timeLeftInMillis = 600000 // Reset to 10 minutes + updateCountDownTimerText() + binding.matterProgressBar.progress = 0 + isTimerRunning = false + } + + private fun startDishwasherTimer() { + if (isTimerRunning) return + + timer = object : CountDownTimer(timeLeftInMillis, 1000) { + override fun onTick(millisUntilFinished: Long) { + timeLeftInMillis = millisUntilFinished + updateCountDownTimerText() + updateMatterProgressBar() + SharedPrefsUtils.saveDishwasherTimeLeftFormatted(dishwasherPref, timeLeftInMillis) + } + + override fun onFinish() { + updateCompletedCycleTextCounter() + binding.btnOff.performClick() + SharedPrefsUtils.saveDishwasherTimeLeftFormatted(dishwasherPref, timeLeftInMillis) + } + }.start() + + isTimerRunning = true + } + + private fun updateMatterProgressBar() { + progress = ((600000 - timeLeftInMillis).toFloat() / 600000 * 100).toInt() + SharedPrefsUtils.saveDishwasherCompletedCycleProgressBar(dishwasherPref, progress) + binding.matterProgressBar.progress = progress.toInt() + } + + private suspend fun getDishwasherDeviceEnergyManagement(): DeviceEnergyManagementCluster { + return DeviceEnergyManagementCluster( + ChipClient.getConnectedDevicePointer(requireContext(), deviceId), + DISHWASHER_DEVICE_ENERGY_MANAGEMENT_ENDPOINT + ) + } + + private suspend fun getEleDevMag(): ElectricalEnergyMeasurementCluster { + return ElectricalEnergyMeasurementCluster( + ChipClient.getConnectedDevicePointer(requireContext(), deviceId), + DISHWASHER_ELECTRICAL_POWER_MEASUREMENT_ENDPOINT + ) + } + + private suspend fun getDishwasherClusterForDevice(): OperationalStateCluster { + return OperationalStateCluster( + ChipClient.getConnectedDevicePointer(requireContext(), deviceId), endpointId + ) + + } + + private suspend fun readEnergy() { + getEleDevMag().subscribeCumulativeEnergyImportedAttribute(object : + ElectricalEnergyMeasurementCluster.CumulativeEnergyImportedAttributeCallback { + override fun onError(error: java.lang.Exception?) { + if (error != null) { + println("Error :${error.message}") + Timber.tag(TAG).e("Error :${error.message}") + if (isAdded) { + requireActivity().runOnUiThread { + Toast.makeText( + requireContext(), + "Error Occurred:${error.message}", + Toast.LENGTH_SHORT + ).show() + } + } + + } + } + + @SuppressLint("SetTextI18n") + override fun onSuccess(value: ChipStructs.ElectricalEnergyMeasurementClusterEnergyMeasurementStruct?) { + if (isAdded) { + requireActivity().runOnUiThread { + val energyValue = value?.energy + if (null != energyValue) { + val str = (energyValue?.toDouble())?.div(1_000_000) + val formattedEnergyValue = String.format("%.3f", str) + if (formattedEnergyValue.isNotEmpty() || formattedEnergyValue.isNotBlank()) { + currentTotalEnergy = formattedEnergyValue.toFloat() + if (mInCurrentCycleGlobalState == 0.000F) { + mInCurrentCycleGlobalState = formattedEnergyValue.toFloat() + } + //currentInCycleEnergy = currentTotalEnergy - currentInCycleEnergy + if(cycleCounter!! <= 0){ + currentInCycleEnergy = + currentTotalEnergy + }else{ + currentInCycleEnergy = + currentTotalEnergy - mInCurrentCycleGlobalState + } + + //in current cycle + if (mCurrentCycleState) { + mCurrentCycleState = false + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${"${String.format("%.3f", currentInCycleEnergy)}"}${" kWh"} " + + ) + } else { + if (cycleCounter!! <= 0) { + // when the completed cycle is zero that is during the first cycle, in current cycle + //will be the Total energy coming from the firmware + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${ + String.format( + "%.3f", currentTotalEnergy + ) + }${" kWh"} " + + ) + SharedPrefsUtils.saveDishwasherInCurrentCycleEnergyConsumed( + dishwasherPref, + String.format("%.3f", currentTotalEnergy).toFloat() + ) + } else { + //lifecycleScope.launch { + //delay(60000) + if(!DishWasherModel.dishwasherCurrentRunningState.equals( + "Stopped", ignoreCase = true + )) { + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${ + String.format( + "%.3f", currentInCycleEnergy + ) + }${" kWh"} " + + ) + //} + SharedPrefsUtils.saveDishwasherInCurrentCycleEnergyConsumed( + dishwasherPref, + String.format("%.3f", currentInCycleEnergy).toFloat() + ) + } + + } + if(DishWasherModel.dishwasherCurrentRunningState.equals( + "Stopped", ignoreCase = true + )) { + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + } + } + + + // in Total + if (cycleCounter!! <= 0) { + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "$formattedEnergyValue${" kWh"} " + + ) + SharedPrefsUtils.saveDishwasherTotalEnergyConsumption( + dishwasherPref, formattedEnergyValue.toFloat() + ) + } else { + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "$formattedEnergyValue${" kWh"} " + + ) + SharedPrefsUtils.saveDishwasherTotalEnergyConsumption( + dishwasherPref, formattedEnergyValue.toFloat() + ) + } + // average energy per cycle + val averageCurrentEnergyPerCycle = + currentTotalEnergy.div(cycleCounter!!) + if (averageCurrentEnergyPerCycle.isInfinite()) { + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + } + if (cycleCounter!! <= 0) { + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${formattedEnergyValue}${" kWh"} " + + ) + SharedPrefsUtils.saveDishwasherAverageEnergyPerCycle( + dishwasherPref, formattedEnergyValue.toFloat() + ) + } else { + /*if (DishWasherModel.dishwasherCurrentRunningState.equals( + "Stopped", ignoreCase = true + ) + ) {*/ + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${ + String.format( + "%.3f", averageCurrentEnergyPerCycle + ) + }${" kWh"} " + + ) + SharedPrefsUtils.saveDishwasherAverageEnergyPerCycle( + dishwasherPref, + String.format("%.3f", averageCurrentEnergyPerCycle) + .toFloat() + ) + //} + } + } else { + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + } + } else { + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + } + + } + } + + } + }, 1, 10) + } + + private suspend fun pauseDishwasher() { + + getDishwasherClusterForDevice().pause(object : + OperationalStateCluster.OperationalCommandResponseCallback { + override fun onError(error: java.lang.Exception?) { + println("Error: ${error!!.message}") + } + + override fun onSuccess(commandResponseState: ChipStructs.OperationalStateClusterErrorStateStruct?) { + imageDishwasherPause() + } + }, TIME_OUT) + + } + + private suspend fun turnOffDishwasher() { + getDishwasherClusterForDevice().stop(object : + OperationalStateCluster.OperationalCommandResponseCallback { + override fun onError(error: java.lang.Exception?) { + println("Error: ${error!!.message}") + } + + override fun onSuccess(commandResponseState: ChipStructs.OperationalStateClusterErrorStateStruct?) { + imageDishwasherOff() + } + + }) + + } + + private suspend fun turnOnDishwasher() { + getDishwasherClusterForDevice().start(object : + OperationalStateCluster.OperationalCommandResponseCallback { + override fun onError(error: java.lang.Exception?) { + println("Error: ${error!!.message}") + } + + override fun onSuccess(commandResponseState: ChipStructs.OperationalStateClusterErrorStateStruct?) { + imageDishwasherOn() + } + + }, TIME_OUT) + } + + private fun imageDishwasherPause() { + if (isAdded) { + requireActivity().runOnUiThread { + binding.btnMatterDeviceState.setImageResource(R.drawable.ic_dishwasher_icon_stateoff) + binding.btnMatterDeviceState.setColorFilter( + ContextCompat.getColor(requireContext(), R.color.silabs_dark_blue), + android.graphics.PorterDuff.Mode.SRC_IN + ) + binding.txtClusterStatus.text = + requireContext().getString(R.string.matter_dishwasher_pause) + } + } + } + + private fun imageDishwasherOff() { + if (isAdded) { + requireActivity().runOnUiThread { + binding.btnMatterDeviceState.setImageResource(R.drawable.ic_dishwasher_icon_stateoff) + binding.btnMatterDeviceState.setColorFilter( + ContextCompat.getColor(requireContext(), R.color.silabs_dark_gray_icon), + android.graphics.PorterDuff.Mode.SRC_IN + ) + binding.txtClusterStatus.text = requireContext().getString(R.string.blinky_tb_off) + isPaused = false + binding.btnPause.text = getString(R.string.matter_dishwasher_pause) + + } + } + } + + private fun imageDishwasherOn() { + if (isAdded) { + requireActivity().runOnUiThread { + binding.btnMatterDeviceState.setImageResource(R.drawable.dishwasher_state_on) + binding.btnMatterDeviceState.setColorFilter( + ContextCompat.getColor(requireContext(), R.color.silabs_dark_blue), + android.graphics.PorterDuff.Mode.SRC_IN + ) + binding.txtClusterStatus.text = requireContext().getString(R.string.blinky_tb_on) + } + } + } + + interface CallBackHandler { + fun onBackHandler() + } + + companion object { + private val TAG = Companion::class.java.simpleName.toString() + const val DISHWASHER_PREF = "DISHWASHER_PREF" + fun newInstance(): MatterDishwasherFragment = MatterDishwasherFragment() + + const val DISHWASHER_OPT_ENDPOINT = 1 + const val DISHWASHER_ELECTRICAL_POWER_MEASUREMENT_ENDPOINT = 2 + const val DISHWASHER_DEVICE_ENERGY_MANAGEMENT_ENDPOINT = 3 + private const val TIME_OUT = 900 + var IS_BACK_BUTTON_CLICKED = false + } + + + override fun onResume() { + super.onResume() + if (IS_BACK_BUTTON_CLICKED) { + IS_BACK_BUTTON_CLICKED = false + scope.launch { + //whenever the dishwasher is in pause or resume state,Call the firmware again + //for throwing the reading again + readEnergyWhenInPauseOrResumeState() + } + try { + if (isAdded) { + scope.launch(Dispatchers.Main) { + if (-1 != SharedPrefsUtils.getDishwasherCompletedCycleCount(dishwasherPref)) { + cycleCounter = + SharedPrefsUtils.getDishwasherCompletedCycleCount(dishwasherPref) + updateCompletedCycleTextCounter() + } + } + when (SharedPrefsUtils.getDishwasherAppliedCycleStates(dishwasherPref)) { + + "Started" -> { + dishwasherStartCycle() + } + + "Stopped" -> { + dishwasherStopCycle() + } + + "Resumed" -> { + dishwasherResumeCycle() + } + + "Paused" -> { + dishwasherPauseCycle() + + } + + else -> { + scope.launch(Dispatchers.Main) { + binding.btnOn.isEnabled = true + binding.btnOff.isEnabled = false + binding.btnPause.isEnabled = false + requireActivity().runOnUiThread { + imageDishwasherOff() + } + } + + } + } + } + } catch (e: Exception) { + println("MatterDishwasherFragment ${e.message}") + } + } + } + + private fun restoreRemainingDishwasgerTime() { + val timeLeft = SharedPrefsUtils.getDishwasherTimeLeftFormatted(dishwasherPref) + + if (timeLeft != 0L) { + timeLeftInMillis = timeLeft + updateCountDownTimerText() + } + } + + private fun dishwasherPauseCycle() { + // Retrieve the time left value from SharedPreferences + scope.launch(Dispatchers.Main) { + binding.btnPause.text = getString(R.string.matter_resume) + binding.btnOn.isEnabled = false + binding.btnOff.isEnabled = true + binding.btnPause.isEnabled = true + restoreRemainingDishwasgerTime() + progress = SharedPrefsUtils.getDishwasherCompletedCycleProgressBar(dishwasherPref) + updateMatterProgressBar() + isPaused = true + imageDishwasherPause() + + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "${ + SharedPrefsUtils.getDishwasherTotalEnergyConsumption( + dishwasherPref + ) + }${" kWh"} " + + ) + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${ + SharedPrefsUtils.getDishwasherAverageEnergyPerCycle( + dishwasherPref + ) + }${" kWh"} " + + ) + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${ + SharedPrefsUtils.getDishwasherInCurrentCycleEnergyConsumed( + dishwasherPref + ) + }${" kWh"} " + + ) + } + + } + + private fun dishwasherResumeCycle() { + // Retrieve the time left value from SharedPreferences + scope.launch(Dispatchers.Main) { + binding.btnPause.text = getString(R.string.matter_dishwasher_pause) + binding.btnOn.isEnabled = false + binding.btnOff.isEnabled = true + binding.btnPause.isEnabled = true + restoreRemainingDishwasgerTime() + isPaused = false + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "${ + SharedPrefsUtils.getDishwasherTotalEnergyConsumption( + dishwasherPref + ) + }${" kWh"} " + + ) + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${ + SharedPrefsUtils.getDishwasherAverageEnergyPerCycle( + dishwasherPref + ) + }${" kWh"} " + + ) + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${ + SharedPrefsUtils.getDishwasherInCurrentCycleEnergyConsumed( + dishwasherPref + ) + }${" kWh"} " + + ) + imageDishwasherResume() + } + + + } + + private fun dishwasherStopCycle() { + scope.launch(Dispatchers.Main) { + isPaused = false + binding.btnOn.isEnabled = true + binding.btnOff.isEnabled = false + binding.btnPause.isEnabled = false + imageDishwasherOff() + stopDishwasherTimer() + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "${ + SharedPrefsUtils.getDishwasherTotalEnergyConsumption( + dishwasherPref + ) + }${" kWh"} " + + ) + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${ + SharedPrefsUtils.getDishwasherAverageEnergyPerCycle( + dishwasherPref + ) + }${" kWh"} " + + ) + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${ + SharedPrefsUtils.getDishwasherInCurrentCycleEnergyConsumed( + dishwasherPref + ) + }${" kWh"} " + ) + } + + } + + private fun dishwasherStartCycle() { + scope.launch(Dispatchers.Main) { + isPaused = false + binding.btnOff.isEnabled = true + binding.btnPause.isEnabled = true + binding.btnOn.isEnabled = false + binding.tvTotalEnergyConsumption.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + binding.tvEnergyMeterReading.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + binding.tvCurrentCycleEnergyComp.text = Html.fromHtml( + "${"0.000"}${" kWh"} " + + ) + imageDishwasherOn() + } + + } + + override fun onDestroyView() { + super.onDestroyView() + if (timer != null) { + timer?.cancel() + } + if (scope != null) { + scope.cancel() + } + } + + +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterDoorFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterDoorFragment.kt index e704fa38..6001239b 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterDoorFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterDoorFragment.kt @@ -1,12 +1,10 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.fragments import android.annotation.SuppressLint -import android.content.Context import android.content.SharedPreferences import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -36,12 +34,11 @@ import com.siliconlabs.bledemo.features.demo.matter_demo.utils.MessageDialogFrag import com.siliconlabs.bledemo.features.demo.matter_demo.utils.SharedPrefsUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.jetbrains.annotations.NotNull import timber.log.Timber +import java.util.Optional class MatterDoorFragment : Fragment() { @@ -51,37 +48,35 @@ class MatterDoorFragment : Fragment() { private val deviceController: ChipDeviceController get() = ChipClient.getDeviceController(requireContext()) private lateinit var mPrefs: SharedPreferences - private var scannedDeviceList = ArrayList() + private lateinit var scope: CoroutineScope private lateinit var binding: FragmentMatterDoorLightBinding private var deviceId: Long = INIT private var endpointId: Int = ON_OFF_CLUSTER_ENDPOINT private lateinit var model: MatterScannedResultModel private var customProgressDialog: CustomProgressDialog? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mPrefs = requireContext().getSharedPreferences( - "your_preference_name", + MatterDemoActivity.MATTER_PREF, AppCompatActivity.MODE_PRIVATE ) if (requireArguments() != null) { model = requireArguments().getParcelable(ARG_DEVICE_MODEL)!! deviceId = model.deviceId - Timber.tag(TAG).e("deviceID: " + model) + Timber.tag(TAG).e("deviceID: $model") } if (deviceId != null) { - showMatterProgressDialog(getString(R.string.please_wait)) - // retrieveSavedDevices() - GlobalScope.launch { + showMatterProgressDialog(getString(R.string.matter_device_status)) + CoroutineScope(Dispatchers.IO).launch { // This code will run asynchronously - val resultq = checkForDeviceStatus() - if (resultq) { + val resulInfo = checkForDeviceStatus() + if (resulInfo) { println("Operation was successful") removeProgress() - // prepareList() - } } } @@ -124,6 +119,7 @@ class MatterDoorFragment : Fragment() { private fun showMatterProgressDialog(message: String) { customProgressDialog = CustomProgressDialog(requireContext()) customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + customProgressDialog!!.setCanceledOnTouchOutside(false) customProgressDialog!!.setMessage(message) customProgressDialog!!.show() } @@ -131,7 +127,7 @@ class MatterDoorFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { binding = FragmentMatterDoorLightBinding.inflate(inflater, container, false) return binding.root } @@ -143,20 +139,21 @@ class MatterDoorFragment : Fragment() { deviceController.setCompletionListener(DoorChipControllerCallback()) binding.btnMatterDeviceState.setImageResource(R.drawable.door_lock) binding.txtClusterName.text = requireContext().getText(R.string.matter_door_title) - binding.btnOn.setLongClickable(false); - binding.btnOff.setLongClickable(false); + binding.btnOn.isLongClickable = false; + binding.btnOff.isLongClickable = false; binding.btnOn.text = requireContext().getText(R.string.matter_locked_status) binding.btnOff.text = requireContext().getText(R.string.matter_unlock_status) binding.btnToggle.visibility = View.GONE binding.btnOn.setOnClickListener { + showMatterProgressDialog(getString(R.string.matter_door_lock_in_progress)) scope.launch { - // showMatterProgressDialog(getString(R.string.please_wait)) sendLockCommandClick() } } binding.btnOff.setOnClickListener { + showMatterProgressDialog(getString(R.string.matter_door_lock_in_progress)) scope.launch { sendUnlockCommandClick() } @@ -168,32 +165,23 @@ class MatterDoorFragment : Fragment() { ) } - override fun onAttach(@NotNull context: Context) { - super.onAttach(context) - } - - override fun onDetach() { - super.onDetach() - } - private fun showMessageDialog() { try { - if (isAdded && requireActivity() != null && !requireActivity().isFinishing) { + if (isAdded && !requireActivity().isFinishing) { requireActivity().runOnUiThread { if (!MessageDialogFragment.isDialogShowing()) { dialog = MessageDialogFragment() dialog.setMessage(getString(R.string.matter_device_offline_text)) dialog.setOnDismissListener { removeProgress() - if (requireActivity().supportFragmentManager.getBackStackEntryCount() > 0) { - requireActivity().supportFragmentManager.popBackStack(); + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + requireActivity().supportFragmentManager.popBackStack() } else { FragmentUtils.getHost( this@MatterDoorFragment, CallBackHandler::class.java - ) - .onBackHandler() + ).onBackHandler() } } val transaction: FragmentTransaction = @@ -206,14 +194,14 @@ class MatterDoorFragment : Fragment() { Timber.e("device offline") } } catch (e: Exception) { - Timber.e("device offline " + e) + Timber.e("device offline :$e") } } private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - if (requireActivity().supportFragmentManager.getBackStackEntryCount() > 0) { + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { requireActivity().supportFragmentManager.popBackStack(); } else { FragmentUtils.getHost(this@MatterDoorFragment, CallBackHandler::class.java) @@ -259,12 +247,11 @@ class MatterDoorFragment : Fragment() { } private suspend fun sendLockCommandClick() { - getLockUnlockClusterForDevice().lockDoor( object : ChipClusters.DefaultClusterCallback { override fun onSuccess() { removeProgress() - Timber.tag(TAG).e("lock command Success") + Timber.tag(TAG).e("Lock command Success") binding.btnMatterDeviceState.setImageResource(R.drawable.door_lock) } @@ -272,18 +259,19 @@ class MatterDoorFragment : Fragment() { removeProgress() SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) showMessageDialog() - } - }, null, + }, Optional.empty(), TIME_OUT ) } + private suspend fun sendUnlockCommandClick() { getLockUnlockClusterForDevice().unlockDoor( object : ChipClusters.DefaultClusterCallback { override fun onSuccess() { + removeProgress() Timber.tag(TAG).e("Unlock command Success") SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) binding.btnMatterDeviceState.setImageResource(R.drawable.door_unlock) @@ -291,12 +279,13 @@ class MatterDoorFragment : Fragment() { @SuppressLint("TimberArgCount") override fun onError(error: Exception?) { + removeProgress() SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) Timber.tag(TAG).e("Unlock command failure: $error") showMessageDialog() } - }, null, + }, Optional.empty(), TIME_OUT ) } @@ -311,7 +300,7 @@ class MatterDoorFragment : Fragment() { inner class DoorChipControllerCallback : GenericChipDeviceListener() { override fun onConnectDeviceComplete() {} - override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { + override fun onCommissioningComplete(nodeId: Long, errorCode: Long) { Timber.tag(TAG).d("onCommissioningComplete for nodeId $nodeId: $errorCode") // showMessage("Address update complete for nodeId $nodeId with code $errorCode") } @@ -324,7 +313,7 @@ class MatterDoorFragment : Fragment() { Timber.tag(TAG).d("onCloseBleComplete") } - override fun onError(error: Throwable) { + override fun onError(error: Throwable?) { super.onError(error) Timber.tag(TAG).d("onError : $error") } @@ -335,7 +324,7 @@ class MatterDoorFragment : Fragment() { } companion object { - private val TAG = MatterDoorFragment.javaClass.simpleName.toString() + private val TAG = Companion::class.java.simpleName.toString() fun newInstance(): MatterDoorFragment = MatterDoorFragment() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterLightFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterLightFragment.kt index c7149797..874bf9f4 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterLightFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterLightFragment.kt @@ -8,7 +8,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment @@ -22,8 +21,6 @@ import com.siliconlabs.bledemo.bluetooth.beacon_utils.eddystone.Constants import com.siliconlabs.bledemo.databinding.FragmentMatterDoorLightBinding import com.siliconlabs.bledemo.features.demo.matter_demo.activities.MatterDemoActivity import com.siliconlabs.bledemo.features.demo.matter_demo.controller.GenericChipDeviceListener -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterTemperatureSensorFragment.Companion.MAX_REFRESH_PERIOD_S -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterTemperatureSensorFragment.Companion.MIN_REFRESH_PERIOD_S import com.siliconlabs.bledemo.features.demo.matter_demo.model.MatterScannedResultModel import com.siliconlabs.bledemo.features.demo.matter_demo.utils.ChipClient import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomProgressDialog @@ -32,7 +29,6 @@ import com.siliconlabs.bledemo.features.demo.matter_demo.utils.MessageDialogFrag import com.siliconlabs.bledemo.features.demo.matter_demo.utils.SharedPrefsUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -55,21 +51,22 @@ class MatterLightFragment : Fragment() { private var currLightStatus: Boolean = false private lateinit var matterLightFragmentJob: Job private val matterLightFragmentScope = CoroutineScope(Dispatchers.Main) - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - mPrefs = requireContext().getSharedPreferences("your_preference_name", Context.MODE_PRIVATE) + mPrefs = requireContext().getSharedPreferences( + MatterDemoActivity.MATTER_PREF, + Context.MODE_PRIVATE + ) model = requireArguments().getParcelable(ARG_DEVICE_MODEL)!! deviceId = model.deviceId Timber.tag(TAG).d("deviceID: $model") - showMatterProgressDialog(getString(R.string.please_wait)) + showMatterProgressDialog(getString(R.string.matter_device_status)) // retrieveSavedDevices() - GlobalScope.launch { + CoroutineScope(Dispatchers.IO).launch { // This code will run asynchronously - val results = checkForDeviceStatus() if (results) { println("Operation was successful") @@ -101,28 +98,6 @@ class MatterLightFragment : Fragment() { }) delay(Constants.SCAN_TIMER * 500) -// if (model.isDeviceOnline) { -// getOnOffClusterForDevice().subscribeOnOffAttribute(object : -// ChipClusters.BooleanAttributeCallback { -// override fun onSuccess(value: Boolean) { -// println("subscribeOnOffAttribute $value") -// Timber.tag(TAG).e("subscribeOnOffAttribute $value") -// currLightStatus = value -// if (currLightStatus) { -// imageForLightOn() -// } else { -// imageForLightOff() -// } -// } -// -// override fun onError(error: java.lang.Exception?) { -// Timber.tag(TAG).e("subscribeOnOffAttribute onError:$error") -// } -// -// }, MIN_REFRESH_PERIOD_S, MAX_REFRESH_PERIOD_S) -// } - - // Return the result (true or false based on the actual result) true } } @@ -140,7 +115,7 @@ class MatterLightFragment : Fragment() { dialog.setOnDismissListener { removeProgress() if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { - requireActivity().supportFragmentManager.popBackStack(); + requireActivity().supportFragmentManager.popBackStack() } else { FragmentUtils.getHost( this@MatterLightFragment, CallBackHandler::class.java @@ -149,18 +124,15 @@ class MatterLightFragment : Fragment() { } val transaction: FragmentTransaction = requireActivity().supportFragmentManager.beginTransaction() - dialog.show(transaction, dialogTag) } - - } } else { Timber.tag(TAG).e("device offline") } } catch (e: Exception) { - Timber.e("" + e) + Timber.e("Exception Occurred $e") } @@ -176,13 +148,14 @@ class MatterLightFragment : Fragment() { customProgressDialog = CustomProgressDialog(requireContext()) customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) customProgressDialog!!.setMessage(message) + customProgressDialog!!.setCanceledOnTouchOutside(false) customProgressDialog!!.show() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { binding = FragmentMatterDoorLightBinding.inflate(inflater, container, false) return binding.root } @@ -192,27 +165,24 @@ class MatterLightFragment : Fragment() { super.onViewCreated(view, savedInstanceState) scope = viewLifecycleOwner.lifecycleScope deviceController.setCompletionListener(LightChipControllerCallback()) - imageForLightOff() - binding.btnOn.setLongClickable(false); - binding.btnOff.setLongClickable(false); + startBackgroundTask() + binding.btnOn.isLongClickable = false + binding.btnOff.isLongClickable = false binding.btnOff.text = requireContext().getText(R.string.matter_light_off_status) binding.btnOn.text = requireContext().getText(R.string.matter_light_on_status) binding.btnToggle.text = requireContext().getText(R.string.matter_light_toggle_status) binding.btnOn.setOnClickListener { scope.launch { - //showMatterProgressDialog(getString(R.string.please_wait)) sendOnCommandClick() } } binding.btnOff.setOnClickListener { - // showMatterProgressDialog(getString(R.string.please_wait)) scope.launch { sendOffCommandClick() } } binding.btnToggle.setOnClickListener { - // showMatterProgressDialog(getString(R.string.please_wait)) scope.launch { sendToggleCommandClicked() } } (activity as MatterDemoActivity).hideQRScanner() @@ -227,15 +197,13 @@ class MatterLightFragment : Fragment() { private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { if (isAdded) { - if (requireActivity().supportFragmentManager.getBackStackEntryCount() > 0) { - requireActivity().supportFragmentManager.popBackStack(); + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + requireActivity().supportFragmentManager.popBackStack() } else { FragmentUtils.getHost(this@MatterLightFragment, CallBackHandler::class.java) .onBackHandler() } } - - } } @@ -258,7 +226,7 @@ class MatterLightFragment : Fragment() { showMessageDialog() } - }) + }, TIME_OUT) } private suspend fun sendOffCommandClick() { @@ -296,7 +264,7 @@ class MatterLightFragment : Fragment() { } private fun imageForLightOff() { - if (requireArguments() != null && isAdded) { + if (isAdded) { requireActivity().runOnUiThread { binding.btnMatterDeviceState.setImageResource(R.drawable.matter_light) binding.btnMatterDeviceState.setColorFilter( @@ -309,7 +277,7 @@ class MatterLightFragment : Fragment() { } private fun imageForLightOn() { - if (requireArguments() != null && isAdded) { + if (isAdded) { requireActivity().runOnUiThread { binding.btnMatterDeviceState.setImageResource(R.drawable.matter_light) binding.btnMatterDeviceState.setColorFilter( @@ -321,17 +289,11 @@ class MatterLightFragment : Fragment() { } } - private fun showMessage(msg: String) { - requireActivity().runOnUiThread { - Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show() - } - } - inner class LightChipControllerCallback : GenericChipDeviceListener() { override fun onConnectDeviceComplete() {} - override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { + override fun onCommissioningComplete(nodeId: Long, errorCode: Long) { Timber.tag(TAG).d("onCommissioningComplete for nodeId $nodeId: $errorCode") // showMessage("Address update complete for nodeId $nodeId with code $errorCode") } @@ -344,7 +306,7 @@ class MatterLightFragment : Fragment() { Timber.tag(TAG).d("onCloseBleComplete") } - override fun onError(error: Throwable) { + override fun onError(error: Throwable?) { super.onError(error) println("MatterLight OnError: $error") Timber.tag(TAG).e("OnError: $error") @@ -360,11 +322,12 @@ class MatterLightFragment : Fragment() { matterLightFragmentJob = matterLightFragmentScope.launch(Dispatchers.IO) { while (true) { delay(1000) - getOnOffClusterForDevice().readOnOffAttribute(object : ChipClusters.BooleanAttributeCallback{ + getOnOffClusterForDevice().readOnOffAttribute(object : + ChipClusters.BooleanAttributeCallback { override fun onSuccess(value: Boolean) { - if (value){ + if (value) { imageForLightOn() - }else{ + } else { imageForLightOff() } } @@ -376,12 +339,14 @@ class MatterLightFragment : Fragment() { } } } + private fun stopBackgroundTask() { if (::matterLightFragmentJob.isInitialized && matterLightFragmentJob.isActive) { matterLightFragmentJob.cancel() println("Background task stopped") } } + override fun onDestroyView() { super.onDestroyView() stopBackgroundTask() @@ -390,12 +355,12 @@ class MatterLightFragment : Fragment() { companion object { private const val TAG = "MatterLightFragment" - public const val ON_OFF_CLUSTER_ENDPOINT = 1 - public const val LEVEL_CONTROL_CLUSTER_ENDPOINT = 1 - public const val ARG_DEVICE_MODEL = "device_model" - public const val ARG_DEVICE_INFO = "device_info" - public const val TIME_OUT = 500 - public const val INIT = 0L + const val ON_OFF_CLUSTER_ENDPOINT = 1 + + const val ARG_DEVICE_MODEL = "device_model" + const val ARG_DEVICE_INFO = "device_info" + const val TIME_OUT = 900 + const val INIT = 0L fun newInstance(): MatterLightFragment = MatterLightFragment() } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterNetworkSelectionInputDialogFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterNetworkSelectionInputDialogFragment.kt index 214341f9..b1caa4b9 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterNetworkSelectionInputDialogFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterNetworkSelectionInputDialogFragment.kt @@ -34,12 +34,13 @@ class MatterNetworkSelectionInputDialogFragment : DialogFragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { binding = DialogNetworkSelectionMatterBinding.inflate(inflater, container, false) if (dialog != null && dialog!!.window != null) { dialog!!.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)); dialog!!.window!!.requestFeature(Window.FEATURE_NO_TITLE); + dialog!!.setCanceledOnTouchOutside(false) } return binding.root } @@ -102,10 +103,6 @@ class MatterNetworkSelectionInputDialogFragment : DialogFragment() { } } - fun stopDisplay() { - dismiss() - } - private fun getScreenWidth(activity: Activity): Int { val size = Point() activity.windowManager.defaultDisplay.getSize(size) diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterOTBRInputDialogFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterOTBRInputDialogFragment.kt index 1b96a57d..ecb3b734 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterOTBRInputDialogFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterOTBRInputDialogFragment.kt @@ -11,17 +11,16 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.Window -import android.view.WindowManager import androidx.fragment.app.DialogFragment import com.siliconlabs.bledemo.databinding.FragmentOtbrInputDialogBinding -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterConnectFragment.Companion.DIALOG_OTBR_INFO +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannerFragment.Companion.DIALOG_OTBR_INFO class MatterOTBRInputDialogFragment : DialogFragment() { private lateinit var binding: FragmentOtbrInputDialogBinding companion object { - public const val WINDOW_SIZE = 0.65 + const val WINDOW_SIZE = 0.65 fun newInstance(): MatterOTBRInputDialogFragment = MatterOTBRInputDialogFragment() } @@ -29,12 +28,13 @@ class MatterOTBRInputDialogFragment : DialogFragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { binding = FragmentOtbrInputDialogBinding.inflate(inflater, container, false) if (dialog != null && dialog!!.window != null) { dialog!!.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) dialog!!.window!!.requestFeature(Window.FEATURE_NO_TITLE); + dialog!!.setCanceledOnTouchOutside(false) } return binding.root } @@ -74,19 +74,10 @@ class MatterOTBRInputDialogFragment : DialogFragment() { } } - fun stopDisplay() { - dismiss() - } private fun getScreenWidth(activity: Activity): Int { val size = Point() activity.windowManager.defaultDisplay.getSize(size) return size.x } - - override fun onDestroy() { - super.onDestroy() - } - - } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterOccupancySensorFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterOccupancySensorFragment.kt index 15850302..6a9466b9 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterOccupancySensorFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterOccupancySensorFragment.kt @@ -5,7 +5,6 @@ import android.content.SharedPreferences import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -21,14 +20,11 @@ import chip.devicecontroller.GetConnectedDeviceCallbackJni import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.bluetooth.beacon_utils.eddystone.Constants import com.siliconlabs.bledemo.databinding.FragmentMatterOccupancySensorBinding -import com.siliconlabs.bledemo.databinding.FragmentMatterThermostatBinding import com.siliconlabs.bledemo.features.demo.matter_demo.activities.MatterDemoActivity import com.siliconlabs.bledemo.features.demo.matter_demo.controller.GenericChipDeviceListener -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ARG_DEVICE_INFO import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ARG_DEVICE_MODEL import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.INIT import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ON_OFF_CLUSTER_ENDPOINT -import com.siliconlabs.bledemo.features.demo.matter_demo.model.CHIPDeviceInfo import com.siliconlabs.bledemo.features.demo.matter_demo.model.MatterScannedResultModel import com.siliconlabs.bledemo.features.demo.matter_demo.utils.ChipClient import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomProgressDialog @@ -45,8 +41,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jetbrains.annotations.NotNull import timber.log.Timber -import java.text.DecimalFormat -import kotlin.Exception class MatterOccupancySensorFragment : Fragment() { @@ -66,7 +60,7 @@ class MatterOccupancySensorFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mPrefs = requireContext().getSharedPreferences( - "your_preference_name", + MatterDemoActivity.MATTER_PREF, AppCompatActivity.MODE_PRIVATE ) if (requireArguments() != null) { @@ -80,7 +74,7 @@ class MatterOccupancySensorFragment : Fragment() { showMatterProgressDialog(getString(R.string.matter_device_status)) // retrieveSavedDevices() - GlobalScope.launch { + CoroutineScope(Dispatchers.IO).launch { // This code will run asynchronously val resultq = checkForDeviceStatus() @@ -107,7 +101,6 @@ class MatterOccupancySensorFragment : Fragment() { model.isDeviceOnline = true SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) // removeProgress() - } override fun onConnectionFailure(nodeId: Long, error: Exception?) { @@ -124,17 +117,9 @@ class MatterOccupancySensorFragment : Fragment() { } } - override fun onAttach(@NotNull context: Context) { - super.onAttach(context) - } - - override fun onDetach() { - super.onDetach() - } - private fun showMessageDialog() { try { - if (isAdded() && requireActivity() != null && !requireActivity().isFinishing) { + if (isAdded && !requireActivity().isFinishing) { requireActivity().runOnUiThread { if (!MessageDialogFragment.isDialogShowing()) { dialog = MessageDialogFragment() @@ -161,14 +146,14 @@ class MatterOccupancySensorFragment : Fragment() { Timber.e("device offline") } } catch (e: Exception) { - Timber.e("" + e) + Timber.e("Exception Occurred :$e") } } private fun removeProgress() { - if (customProgressDialog?.isShowing() == true) { + if (customProgressDialog?.isShowing == true) { customProgressDialog?.dismiss() } } @@ -177,13 +162,14 @@ class MatterOccupancySensorFragment : Fragment() { customProgressDialog = CustomProgressDialog(requireContext()) customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) customProgressDialog!!.setMessage(message) + customProgressDialog!!.setCanceledOnTouchOutside(false) customProgressDialog!!.show() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { binding = FragmentMatterOccupancySensorBinding.inflate(inflater, container, false) return binding.root @@ -263,7 +249,7 @@ class MatterOccupancySensorFragment : Fragment() { override fun onError(error: Exception?) { SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) - Timber.tag(TAG).e( "error readOccupancySensorType " + error) + Timber.tag(TAG).e( "error readOccupancySensorType :$error" ) showMessageDialog() } }) @@ -286,7 +272,7 @@ class MatterOccupancySensorFragment : Fragment() { inner class OccupancySensorChipControllerCallback : GenericChipDeviceListener() { override fun onConnectDeviceComplete() {} - override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { + override fun onCommissioningComplete(nodeId: Long, errorCode: Long) { Timber.tag(TAG).d( "onCommissioningComplete for nodeId $nodeId: $errorCode") //showMessage("Address update complete for nodeId $nodeId with code $errorCode") } @@ -299,7 +285,7 @@ class MatterOccupancySensorFragment : Fragment() { Timber.tag(TAG).d( "onCloseBleComplete") } - override fun onError(error: Throwable) { + override fun onError(error: Throwable?) { super.onError(error) Timber.tag(TAG).d( "onError : $error") } @@ -310,7 +296,7 @@ class MatterOccupancySensorFragment : Fragment() { } companion object { - private val TAG = MatterOccupancySensorFragment.javaClass.simpleName.toString() + private val TAG = Companion::class.java.simpleName.toString() fun newInstance(): MatterOccupancySensorFragment = MatterOccupancySensorFragment() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterPlugFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterPlugFragment.kt index cc454c32..19fe710c 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterPlugFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterPlugFragment.kt @@ -1,11 +1,10 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.fragments -import android.content.Context +import android.annotation.SuppressLint import android.content.SharedPreferences import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -33,11 +32,9 @@ import com.siliconlabs.bledemo.features.demo.matter_demo.utils.MessageDialogFrag import com.siliconlabs.bledemo.features.demo.matter_demo.utils.SharedPrefsUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.jetbrains.annotations.NotNull import timber.log.Timber @@ -58,24 +55,24 @@ class MatterPlugFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mPrefs = requireContext().getSharedPreferences( - "your_preference_name", + MatterDemoActivity.MATTER_PREF, AppCompatActivity.MODE_PRIVATE ) if (requireArguments() != null) { model = requireArguments().getParcelable(ARG_DEVICE_MODEL)!! deviceId = model.deviceId - Timber.tag(TAG).e( "deviceID: " + model) + Timber.tag(TAG).e("deviceID: $model") } if (deviceId != null) { - showMatterProgressDialog(getString(R.string.please_wait)) + showMatterProgressDialog(getString(R.string.matter_device_status)) // retrieveSavedDevices() - GlobalScope.launch { + CoroutineScope(Dispatchers.IO).launch { // This code will run asynchronously - val resultq = checkForDeviceStatus() - if (resultq) { + val resultInfo = checkForDeviceStatus() + if (resultInfo) { println("Operation was successful") removeProgress() } @@ -86,7 +83,7 @@ class MatterPlugFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { binding = FragmentMatterPlugBinding.inflate(inflater, container, false) return binding.root } @@ -107,13 +104,13 @@ class MatterPlugFragment : Fragment() { removeProgress() SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) showMessageDialog() - requireActivity().runOnUiThread(Runnable { + requireActivity().runOnUiThread { binding.btnMatterDeviceState.setImageResource(R.drawable.matter_plug_off) binding.btnMatterDeviceState.setColorFilter( ContextCompat.getColor(requireContext(), R.color.silabs_grey), android.graphics.PorterDuff.Mode.SRC_IN ) - }) + } } }) @@ -125,31 +122,22 @@ class MatterPlugFragment : Fragment() { } } - override fun onAttach(@NotNull context: Context) { - super.onAttach(context) - } - - override fun onDetach() { - super.onDetach() - } - private fun showMessageDialog() { try { - if (isAdded() && requireActivity() != null && !requireActivity().isFinishing) { + if (isAdded && !requireActivity().isFinishing) { requireActivity().runOnUiThread { if (!MessageDialogFragment.isDialogShowing()) { dialog = MessageDialogFragment() dialog.setMessage(getString(R.string.matter_device_offline_text)) dialog.setOnDismissListener { removeProgress() - if (requireActivity().supportFragmentManager.getBackStackEntryCount() > 0) { - requireActivity().supportFragmentManager.popBackStack(); + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + requireActivity().supportFragmentManager.popBackStack() } else { FragmentUtils.getHost( this@MatterPlugFragment, CallBackHandler::class.java - ) - .onBackHandler() + ).onBackHandler() } } val transaction: FragmentTransaction = @@ -162,14 +150,14 @@ class MatterPlugFragment : Fragment() { Timber.e("device offline") } } catch (e: Exception) { - Timber.e("" + e) + Timber.e("Exception Occurred: $e") } } private fun removeProgress() { - if (customProgressDialog?.isShowing() == true) { + if (customProgressDialog?.isShowing == true) { customProgressDialog?.dismiss() } } @@ -178,6 +166,7 @@ class MatterPlugFragment : Fragment() { customProgressDialog = CustomProgressDialog(requireContext()) customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) customProgressDialog!!.setMessage(message) + customProgressDialog!!.setCanceledOnTouchOutside(false) customProgressDialog!!.show() } @@ -186,8 +175,8 @@ class MatterPlugFragment : Fragment() { scope = viewLifecycleOwner.lifecycleScope deviceController.setCompletionListener(LightChipControllerCallback()) binding.btnMatterDeviceState.setImageResource(R.drawable.matter_plug_off) - binding.btnOn.setLongClickable(false); - binding.btnOff.setLongClickable(false); + binding.btnOn.isLongClickable = false + binding.btnOff.isLongClickable = false binding.btnOff.text = requireContext().getText(R.string.matter_light_off_status) binding.btnOn.text = requireContext().getText(R.string.matter_light_on_status) @@ -210,8 +199,8 @@ class MatterPlugFragment : Fragment() { private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - if (requireActivity().supportFragmentManager.getBackStackEntryCount() > 0) { - requireActivity().supportFragmentManager.popBackStack(); + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + requireActivity().supportFragmentManager.popBackStack() } else { FragmentUtils.getHost(this@MatterPlugFragment, CallBackHandler::class.java) .onBackHandler() @@ -231,16 +220,17 @@ class MatterPlugFragment : Fragment() { getPlugCluster().on(object : ChipClusters.DefaultClusterCallback { override fun onSuccess() { SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) - requireActivity().runOnUiThread(Runnable { + requireActivity().runOnUiThread { binding.btnMatterDeviceState.setImageResource(R.drawable.matter_plug_on) - }) + } } + @SuppressLint("TimberArgCount") override fun onError(ex: Exception) { removeProgress() SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) - Timber.tag(TAG).e( "ON command failure", ex) + Timber.tag(TAG).e("ON command failure: $ex") showMessageDialog() } @@ -251,16 +241,15 @@ class MatterPlugFragment : Fragment() { getPlugCluster().off(object : ChipClusters.DefaultClusterCallback { override fun onSuccess() { SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) - requireActivity().runOnUiThread(Runnable { + requireActivity().runOnUiThread { binding.btnMatterDeviceState.setImageResource(R.drawable.matter_plug_off) - }) - + } } override fun onError(ex: Exception) { removeProgress() SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) - Timber.tag(TAG).e( "OFF command failure", ex) + Timber.tag(TAG).e("OFF command failure :$ex") showMessageDialog() } }) @@ -281,22 +270,22 @@ class MatterPlugFragment : Fragment() { inner class LightChipControllerCallback : GenericChipDeviceListener() { override fun onConnectDeviceComplete() {} - override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { - Timber.tag(TAG).d( "onCommissioningComplete for nodeId $nodeId: $errorCode") + override fun onCommissioningComplete(nodeId: Long, errorCode: Long) { + Timber.tag(TAG).d("onCommissioningComplete for nodeId $nodeId: $errorCode") //showMessage("Address update complete for nodeId $nodeId with code $errorCode") } override fun onNotifyChipConnectionClosed() { - Timber.tag(TAG).d( "onNotifyChipConnectionClosed") + Timber.tag(TAG).d("onNotifyChipConnectionClosed") } override fun onCloseBleComplete() { - Timber.tag(TAG).d( "onCloseBleComplete") + Timber.tag(TAG).d("onCloseBleComplete") } - override fun onError(error: Throwable) { + override fun onError(error: Throwable?) { super.onError(error) - Timber.tag(TAG).d( "onError: $error") + Timber.tag(TAG).d("onError: $error") } } @@ -307,7 +296,7 @@ class MatterPlugFragment : Fragment() { companion object { private const val TAG = "MatterPlugFragment" - public const val ARG_DEVICE_MODEL = "device_model" + const val ARG_DEVICE_MODEL = "device_model" fun newInstance(): MatterPlugFragment = MatterPlugFragment() } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterScannedResultFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterScannedResultFragment.kt index 42e6a8e9..17b4f473 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterScannedResultFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterScannedResultFragment.kt @@ -39,9 +39,9 @@ class MatterScannedResultFragment : Fragment() { private lateinit var scope: CoroutineScope private lateinit var deviceList: ArrayList private lateinit var binding: FragmentMatterScannedResultsBinding - private var deviceId: Long = 0; - private var deviceType: Int = 0; - private var pos: Int = 0; + private var deviceId: Long = 0 + private var deviceType: Int = 0 + private var pos: Int = 0 private lateinit var matterAdapter: MatterScannedResultAdapter private lateinit var mPrefs: SharedPreferences @@ -57,7 +57,7 @@ class MatterScannedResultFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { super.onCreateView(inflater, container, savedInstanceState) scope = viewLifecycleOwner.lifecycleScope binding = FragmentMatterScannedResultsBinding.inflate(inflater, container, false) @@ -113,7 +113,10 @@ class MatterScannedResultFragment : Fragment() { binding.tvQuickStartHyperlink.movementMethod = LinkMovementMethod.getInstance() setListDisplay() - requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressedCallback) + requireActivity().onBackPressedDispatcher.addCallback( + viewLifecycleOwner, + onBackPressedCallback + ) } @@ -123,7 +126,7 @@ class MatterScannedResultFragment : Fragment() { if (deviceList.size !== 0) { Timber.tag(TAG).e("+ deviceList $deviceList") matterAdapter = MatterScannedResultAdapter(deviceList) - binding.recyclerViewScannedDevices.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); + binding.recyclerViewScannedDevices.scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY binding.recyclerViewScannedDevices.adapter = matterAdapter binding.recyclerViewScannedDevices.addItemDecoration( RecyclerViewMargin(resources.getDimensionPixelSize(R.dimen.matter_margin)) @@ -151,7 +154,6 @@ class MatterScannedResultFragment : Fragment() { .setPositiveButton(ok, dialogClickListener) .setNegativeButton(cancel, dialogClickListener).show() } - }) } else { binding.placeholder.visibility = View.VISIBLE @@ -189,7 +191,7 @@ class MatterScannedResultFragment : Fragment() { private fun navigateToDemos(model: MatterScannedResultModel) { - deviceType = model.deviceType!! + deviceType = model.deviceType when (deviceType) { THERMOSTAT_TYPE -> { @@ -200,7 +202,7 @@ class MatterScannedResultFragment : Fragment() { ).navigateToDemo(matterThermostatFragment, model) } - LIGHTNING_TYPE, ENHANCED_COLOR_LIGHT_TYPE, ONOFF_LIGHT_TYPE, TEMPERATURE_COLOR_LIGHT_TYPE -> { + DIMMABLE_LIGHT_TYPE, ENHANCED_COLOR_LIGHT_TYPE, ON_OFF_LIGHT_TYPE, COLOR_TEMPERATURE_LIGHT_TYPE -> { val matterLightFragment = MatterLightFragment.newInstance() FragmentUtils.getHost( this@MatterScannedResultFragment, @@ -208,7 +210,7 @@ class MatterScannedResultFragment : Fragment() { ).navigateToDemo(matterLightFragment, model) } - WINDOW_TYPE -> { + WINDOW_COVERING_TYPE -> { val matterWindowCoverFragment = MatterWindowCoverFragment.newInstance() FragmentUtils.getHost( this@MatterScannedResultFragment, @@ -216,7 +218,7 @@ class MatterScannedResultFragment : Fragment() { ).navigateToDemo(matterWindowCoverFragment, model) } - LOCK_TYPE -> { + DOOR_LOCK_TYPE -> { val matterDoorFragment = MatterDoorFragment.newInstance() FragmentUtils.getHost( this@MatterScannedResultFragment, @@ -248,15 +250,22 @@ class MatterScannedResultFragment : Fragment() { ).navigateToDemo(matterTemperatureSensorFragment, model) } - PLUG_TYPE -> { + DIMMABLE_PLUG_IN_UNIT_TYPE -> { val matterPlugFragment = MatterPlugFragment.newInstance() FragmentUtils.getHost( this@MatterScannedResultFragment, - Callback:: - class.java + Callback::class.java ).navigateToDemo(matterPlugFragment, model) } + DISHWASHER_TYPE -> { + val matterDishwasherFragment = MatterDishwasherFragment.newInstance() + FragmentUtils.getHost( + this@MatterScannedResultFragment, + Callback::class.java + ).navigateToDemo(matterDishwasherFragment, model) + } + else -> { println("Unhandled Operation....") } @@ -271,11 +280,6 @@ class MatterScannedResultFragment : Fragment() { } } - override fun onDestroy() { - super.onDestroy() - - } - interface Callback { fun navigateToDemo( @@ -289,18 +293,73 @@ class MatterScannedResultFragment : Fragment() { private const val ARG_DEVICE_LIST = "device_list" private const val TAG = "MatterScannedResultFragment" private const val MATTER_URL = "https://docs.silabs.com/matter/2.1.0/matter-overview" - public const val THERMOSTAT_TYPE = 769 - public const val LIGHTNING_TYPE = 257 - public const val WINDOW_TYPE = 514 - public const val LOCK_TYPE = 10 - public const val ENHANCED_COLOR_LIGHT_TYPE = 269 - public const val ONOFF_LIGHT_TYPE = 256 - public const val TEMPERATURE_COLOR_LIGHT_TYPE = 268 - // public const val SWITCH_TYPE = 259 - public const val OCCUPANCY_SENSOR_TYPE = 263 - public const val TEMPERATURE_SENSOR_TYPE = 770 - public const val CONTACT_SENSOR_TYPE = 21 - public const val PLUG_TYPE = 267 + + + //Lighting Type + const val ON_OFF_LIGHT_TYPE = 256 + const val DIMMABLE_LIGHT_TYPE = 257 + const val COLOR_TEMPERATURE_LIGHT_TYPE = 268 + const val ENHANCED_COLOR_LIGHT_TYPE = 269 + + //smart plugs/outlets and other actuators + const val ON_OFF_PLUG_IN_UNIT_TYPE = 266 + const val DIMMABLE_PLUG_IN_UNIT_TYPE = 267 + const val PUMP = 771 + + //switches and controls + const val ON_OFF_LIGHT_SWITCH = 259 + const val DIMMER_SWITCH = 260 + const val COLOR_DIMMER_SWITCH = 261 + const val CONTROL_BRIDGE = 2112 + const val PUMP_CONTROLLER = 772 + const val GENERIC_SWITCH = 15 + + //sensors + const val CONTACT_SENSOR_TYPE = 21 + const val LIGHT_SENSOR_TYPE = 262 + const val OCCUPANCY_SENSOR_TYPE = 263 + const val TEMPERATURE_SENSOR_TYPE = 770 + const val PRESSURE_SENSOR_TYPE = 773 + const val FLOW_SENSOR_TYPE = 774 + const val HUMIDITY_SENSOR_TYPE = 775 + const val ON_OFF_SENSOR_TYPE = 2128 + const val SMOKE_CO_ALARM_TYPE = 118 + + //closures + const val DOOR_LOCK_TYPE = 10 + const val DOOR_LOCK_CONTROLLER_TYPE = 11 + const val WINDOW_COVERING_TYPE = 514 + const val WINDOW_COVERING_CONTROLLER_TYPE = 515 + + //HVAC + const val HEATING_COOLING_UNIT_TYPE = 768 + const val THERMOSTAT_TYPE = 769 + const val FAN_TYPE = 43 + const val AIR_PURIFIER_TYPE = 45 + const val AIR_QUALITY_SENSOR_TYPE = 44 + + //media + const val BASIC_VIDEO_PLAYER_TYPE = 40 + const val CASTING_VIDEO_PLAYER_TYPE = 35 + const val SPEAKER_TYPE = 34 + const val CONTENT_APP_TYPE = 36 + const val CASTING_VIDEO_CLIENT_TYPE = 41 + const val VIDEO_REMOTE_CONTROL_TYPE = 42 + + //Generic + const val MODE_SELECT_TYPE = 39 + + //robotic devices + const val ROBOTIC_VACUUM_CLEANER_TYPE = 116 + + //appliances + const val REFRIGERATOR_TYPE = 112 + const val TEMPERATURE_CONTROLLED_CABINET_TYPE = 113 + const val ROOM_AIR_CONDITIONER_TYPE =114 + const val LAUNDRY_WASHER_TYPE = 115 + const val DISHWASHER_TYPE = 117 + + //Hyperlink const private const val HYPERLINK_START = 89 private const val HYPERLINK_END = 107 diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterScannerFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterScannerFragment.kt index 93c046fc..cc715023 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterScannerFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterScannerFragment.kt @@ -2,9 +2,15 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.fragments import android.Manifest import android.annotation.SuppressLint -import android.bluetooth.BluetoothAdapter +import android.app.Activity +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothGatt +import android.content.DialogInterface import android.content.Intent +import android.content.SharedPreferences import android.content.pm.PackageManager +import android.graphics.Color +import android.graphics.drawable.ColorDrawable import android.os.Build import android.os.Bundle import android.os.Handler @@ -16,6 +22,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity.MODE_PRIVATE import androidx.camera.core.AspectRatio import androidx.camera.core.CameraSelector import androidx.camera.core.ImageAnalysis @@ -24,8 +31,13 @@ import androidx.camera.core.Preview import androidx.camera.lifecycle.ProcessCameraProvider import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction -import chip.setuppayload.SetupPayload +import androidx.lifecycle.lifecycleScope +import chip.devicecontroller.ChipClusters +import chip.devicecontroller.ChipDeviceController +import chip.devicecontroller.ChipStructs +import chip.devicecontroller.NetworkCredentials import chip.setuppayload.SetupPayloadParser import com.google.mlkit.vision.barcode.BarcodeScanner import com.google.mlkit.vision.barcode.BarcodeScanning @@ -34,11 +46,38 @@ import com.google.mlkit.vision.common.InputImage import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.databinding.FragmentMatterScannerBinding import com.siliconlabs.bledemo.features.demo.matter_demo.activities.MatterDemoActivity +import com.siliconlabs.bledemo.features.demo.matter_demo.controller.GenericChipDeviceListener +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ON_OFF_CLUSTER_ENDPOINT +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.CONTACT_SENSOR_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.DISHWASHER_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.ENHANCED_COLOR_LIGHT_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.DIMMABLE_LIGHT_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.DOOR_LOCK_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.OCCUPANCY_SENSOR_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.ON_OFF_LIGHT_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.DIMMABLE_PLUG_IN_UNIT_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.COLOR_TEMPERATURE_LIGHT_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.TEMPERATURE_SENSOR_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.THERMOSTAT_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.WINDOW_COVERING_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.manager.BluetoothManager import com.siliconlabs.bledemo.features.demo.matter_demo.model.CHIPDeviceInfo -import com.siliconlabs.bledemo.features.demo.matter_demo.model.ProvisionNetworkType +import com.siliconlabs.bledemo.features.demo.matter_demo.model.MatterScannedResultModel +import com.siliconlabs.bledemo.features.demo.matter_demo.model.NetworkCredentialsParcelable +import com.siliconlabs.bledemo.features.demo.matter_demo.utils.ChipClient +import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomInputDialog +import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomProgressDialog +import com.siliconlabs.bledemo.features.demo.matter_demo.utils.DeviceIDUtil import com.siliconlabs.bledemo.features.demo.matter_demo.utils.FragmentUtils import com.siliconlabs.bledemo.features.demo.matter_demo.utils.MessageDialogFragment +import com.siliconlabs.bledemo.features.demo.matter_demo.utils.SharedPrefsUtils import com.siliconlabs.bledemo.features.iop_test.utils.DialogDeviceInfoFragment +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import matter.onboardingpayload.OnboardingPayload +import matter.onboardingpayload.OnboardingPayloadParser import timber.log.Timber import java.util.concurrent.Executors import kotlin.math.abs @@ -47,22 +86,44 @@ import kotlin.math.min class MatterScannerFragment : Fragment() { - private lateinit var dialog: MessageDialogFragment + + private var customProgressDialog: CustomProgressDialog? = null val dialogTag = "MessageDialog" - private lateinit var binding: FragmentMatterScannerBinding - var networkSelectionDialog: MatterNetworkSelectionInputDialogFragment? = null - private lateinit var networkMode: String - private lateinit var payload: SetupPayload + private var networkSelectionDialog: MatterNetworkSelectionInputDialogFragment? = null private var isShortDiscriminator = false - private lateinit var qrCodeManualInput: String private var deviceDialog: DialogDeviceInfoFragment? = null - private var deviceInfodialogShown = false + private var deviceInfoDialogShown = false + private var otbrEntryDialog: MatterOTBRInputDialogFragment? = null + private var wifiEntryDialog: MatterWifiInputDialogFragment? = null + private var gatt: BluetoothGatt? = null + private var alertDialog: android.app.AlertDialog? = null + private var cameraProvider: ProcessCameraProvider? = null + + private var deviceId: Long = 0 + private lateinit var typeNtw: String + private lateinit var networkCredential: NetworkCredentialsParcelable + private lateinit var scope: CoroutineScope + private lateinit var bluetoothManager: BluetoothManager + private lateinit var networkMode: String + private lateinit var payload: OnboardingPayload + private lateinit var binding: FragmentMatterScannerBinding + private lateinit var dialog: MessageDialogFragment + private lateinit var matterScanDevice: BluetoothDevice + private lateinit var deviceInfo: CHIPDeviceInfo + private lateinit var deviceController: ChipDeviceController + private lateinit var qrCodeManualInput: String + private var scannedDeviceList = ArrayList() + private lateinit var mPrefs: SharedPreferences + @RequiresApi(Build.VERSION_CODES.S) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - + deviceController = ChipClient.getDeviceController(requireContext()) + mPrefs = requireContext().getSharedPreferences( + MatterDemoActivity.MATTER_PREF, MODE_PRIVATE + ) if (!hasLocationPermission()) { requestLocationPermission() } @@ -70,16 +131,16 @@ class MatterScannerFragment : Fragment() { if (!hasCameraPermission()) { requestCameraPermission() } - } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) (activity as MatterDemoActivity).showQRScanner() + scope = viewLifecycleOwner.lifecycleScope binding.submitEntry.setOnClickListener { qrCodeManualInput = binding.manualCodeEditText.text.toString().trim() - println("Input MT : ${qrCodeManualInput}") + println("Input MT : $qrCodeManualInput") handleManualInput(qrCodeManualInput) } } @@ -91,10 +152,8 @@ class MatterScannerFragment : Fragment() { } override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { super.onCreateView(inflater, container, savedInstanceState) binding = FragmentMatterScannerBinding.inflate(inflater, container, false) return binding.root @@ -103,24 +162,20 @@ class MatterScannerFragment : Fragment() { private fun startCamera() { Timber.tag(TAG).e("startCamera") val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext()) - cameraProviderFuture.addListener(Runnable { - val cameraProvider = cameraProviderFuture.get() + cameraProviderFuture.addListener({ + cameraProvider = cameraProviderFuture.get() val metrics = DisplayMetrics().also { binding.cameraView.display?.getRealMetrics(it) } val screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels) if (binding.cameraView.display.rotation != null) { - val preview: Preview = Preview.Builder() - .setTargetAspectRatio(screenAspectRatio) - .setTargetRotation(binding.cameraView.display.rotation) - .build() + val preview: Preview = Preview.Builder().setTargetAspectRatio(screenAspectRatio) + .setTargetRotation(binding.cameraView.display.rotation).build() preview.setSurfaceProvider(binding.cameraView.surfaceProvider) // Setup barcode scanner - val imageAnalysis = ImageAnalysis.Builder() - .setTargetAspectRatio(screenAspectRatio) - .setTargetRotation(binding.cameraView.display.rotation) - .build() + val imageAnalysis = ImageAnalysis.Builder().setTargetAspectRatio(screenAspectRatio) + .setTargetRotation(binding.cameraView.display.rotation).build() val cameraExecutor = Executors.newSingleThreadExecutor() val barcodeScanner: BarcodeScanner = BarcodeScanning.getClient() imageAnalysis.setAnalyzer(cameraExecutor) { imageProxy -> @@ -128,8 +183,8 @@ class MatterScannerFragment : Fragment() { } val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA try { - cameraProvider.unbindAll() - cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis) + cameraProvider?.unbindAll() + cameraProvider?.bindToLifecycle(this, cameraSelector, preview, imageAnalysis) } catch (exc: Exception) { Timber.tag(TAG).e("Use case binding failed: $exc") } @@ -138,71 +193,73 @@ class MatterScannerFragment : Fragment() { }, ContextCompat.getMainExecutor(requireContext())) } + private fun stopCamera() { + cameraProvider?.unbindAll() + } + @SuppressLint("UnsafeOptInUsageError") private fun processImageProxy( - barcodeScanner: BarcodeScanner, - imageProxy: ImageProxy + barcodeScanner: BarcodeScanner, imageProxy: ImageProxy ) { val inputImage = InputImage.fromMediaImage(imageProxy.image!!, imageProxy.imageInfo.rotationDegrees) - barcodeScanner.process(inputImage) - .addOnSuccessListener { barcode -> - Timber.tag(TAG).e("Barcodes" + barcode) - barcode.forEach { - handleScannedQrCode(it) - } - } - .addOnFailureListener { - Timber.tag(TAG).e(it.message ?: it.toString()) - } - .addOnCompleteListener { - imageProxy.close() + barcodeScanner.process(inputImage).addOnSuccessListener { barcode -> + //Timber.tag(TAG).e("Barcodes: $barcode") + barcode.forEach { + handleScannedQrCode(it) } + }.addOnFailureListener { + Timber.tag(TAG).e(it.message ?: it.toString()) + }.addOnCompleteListener { + imageProxy.close() + } } private fun handleManualInput(qrCode: String) { - if (qrCode.isNullOrBlank()) { + if (qrCode.isBlank()) { showMessage(getString(R.string.matter_validation_manual_qrcode)) return } try { - payload = SetupPayloadParser().parseQrCode(qrCode) + payload = if (qrCode.startsWith("MT:")) { + OnboardingPayloadParser().parseQrCode(qrCode) + } else { + OnboardingPayloadParser().parseManualPairingCode(qrCode) + } println("Payload : $payload") if (networkSelectionDialog == null) { - val prev = - requireActivity().supportFragmentManager.findFragmentByTag( - ALERT_NTW_MODE_DIALOG_TAG - ) + val prev = requireActivity().supportFragmentManager.findFragmentByTag( + ALERT_NTW_MODE_DIALOG_TAG + ) if (prev == null) { val chipDeviceInfo = """ - Version: ${payload.version ?: "N/A"} + Version: ${payload.version} - Vendor ID: ${payload.vendorId ?: "N/A"} + Vendor ID: ${payload.vendorId} - Product ID: ${payload.productId ?: "N/A"} + Product ID: ${payload.productId} - Discriminator: ${payload.discriminator ?: "N/A"} + Discriminator: ${payload.discriminator} - Setup PIN Code: ${payload.setupPinCode ?: "N/A"} + Setup PIN Code: ${payload.setupPinCode} Discovery Capabilities: ${ - payload.discoveryCapabilities?.joinToString(", ") ?: "N/A" + payload.discoveryCapabilities.joinToString(", ") } - Commissioning Flow: ${payload.commissioningFlow ?: "N/A"} + Commissioning Flow: ${payload.commissioningFlow} """.trimIndent() showDeviceInfoDialog(chipDeviceInfo) - } } } catch (ex: SetupPayloadParser.SetupPayloadException) { try { - payload = SetupPayloadParser().parseManualEntryCode(qrCode) + payload = OnboardingPayloadParser().parseManualPairingCode(qrCode) isShortDiscriminator = true } catch (ex: Exception) { Timber.tag(TAG).e("Unrecognized Manual Pairing Code $ex") @@ -223,32 +280,32 @@ class MatterScannerFragment : Fragment() { Handler(Looper.getMainLooper()).post { try { - payload = SetupPayloadParser().parseQrCode(barcode.displayValue) + payload = OnboardingPayloadParser().parseQrCode(barcode.displayValue!!) + isShortDiscriminator = true if (networkSelectionDialog == null) { - val prev = - requireActivity().supportFragmentManager.findFragmentByTag( - ALERT_NTW_MODE_DIALOG_TAG - ) + val prev = requireActivity().supportFragmentManager.findFragmentByTag( + ALERT_NTW_MODE_DIALOG_TAG + ) if (prev == null) { val chipDeviceInfo = """ - Version: ${payload.version ?: "N/A"} + Version: ${payload.version} - Vendor ID: ${payload.vendorId ?: "N/A"} + Vendor ID: ${payload.vendorId} - Product ID: ${payload.productId ?: "N/A"} + Product ID: ${payload.productId} - Discriminator: ${payload.discriminator ?: "N/A"} + Discriminator: ${payload.discriminator} - Setup PIN Code: ${payload.setupPinCode ?: "N/A"} + Setup PIN Code: ${payload.setupPinCode} Discovery Capabilities: ${ - payload.discoveryCapabilities?.joinToString(", ") ?: "N/A" + payload.discoveryCapabilities.joinToString(", ") } - Commissioning Flow: ${payload.commissioningFlow ?: "N/A"} + Commissioning Flow: ${payload.commissioningFlow} """.trimIndent() showDeviceInfoDialog(chipDeviceInfo) @@ -274,7 +331,7 @@ class MatterScannerFragment : Fragment() { private fun showMessageDialog(string: String) { try { - if (isAdded && requireActivity() != null && !requireActivity().isFinishing) { + if (isAdded && !requireActivity().isFinishing) { requireActivity().runOnUiThread { if (!MessageDialogFragment.isDialogShowing()) { dialog = MessageDialogFragment() @@ -292,34 +349,11 @@ class MatterScannerFragment : Fragment() { Timber.e("Unrecognized QR Exception") } } catch (e: Exception) { - Timber.e("Unrecognized QR Exception $e" ) + Timber.e("Unrecognized QR Exception $e") } } - private fun navigateToThreadDevice(setupPayload: SetupPayload, isShortDiscriminator: Boolean) { - FragmentUtils.getHost(this@MatterScannerFragment, CallBack::class.java) - .setNetworkType(ProvisionNetworkType.THREAD) - FragmentUtils.getHost(this@MatterScannerFragment, CallBack::class.java) - .onChipDeviceInfoReceived( - CHIPDeviceInfo.fromSetupPayload( - setupPayload, - isShortDiscriminator - ), INPUT_NETWORK_THREAD_TYPE_SELECTED - ) - } - - private fun navigateToWiFiDevice(setupPayload: SetupPayload, isShortDiscriminator: Boolean) { - FragmentUtils.getHost(this@MatterScannerFragment, CallBack::class.java) - .setNetworkType(ProvisionNetworkType.WIFI) - FragmentUtils.getHost(this@MatterScannerFragment, CallBack::class.java) - .onChipDeviceInfoReceived( - CHIPDeviceInfo.fromSetupPayload( - setupPayload, - isShortDiscriminator - ), INPUT_NETWORK_WIFI_TYPE_SELECTED - ) - } private fun aspectRatio(width: Int, height: Int): Int { val previewRatio = max(width, height).toDouble() / min(width, height) @@ -331,8 +365,7 @@ class MatterScannerFragment : Fragment() { private fun hasCameraPermission(): Boolean { return (PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission( - requireContext(), - Manifest.permission.CAMERA + requireContext(), Manifest.permission.CAMERA )) } @@ -352,20 +385,16 @@ class MatterScannerFragment : Fragment() { } private fun hasLocationPermission(): Boolean { - val locationPermissionGranted = - ContextCompat.checkSelfPermission( - requireContext(), - Manifest.permission.ACCESS_FINE_LOCATION - ) == PackageManager.PERMISSION_GRANTED + val locationPermissionGranted = ContextCompat.checkSelfPermission( + requireContext(), Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED // Android 12 new permission var bleScanPermissionGranted = true if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - bleScanPermissionGranted = - ContextCompat.checkSelfPermission( - requireContext(), - Manifest.permission.BLUETOOTH_SCAN - ) == PackageManager.PERMISSION_GRANTED + bleScanPermissionGranted = ContextCompat.checkSelfPermission( + requireContext(), Manifest.permission.BLUETOOTH_SCAN + ) == PackageManager.PERMISSION_GRANTED } return locationPermissionGranted && bleScanPermissionGranted @@ -373,9 +402,7 @@ class MatterScannerFragment : Fragment() { @Deprecated("Deprecated in Java") override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { if (requestCode == REQUEST_CODE_CAMERA_PERMISSION) { if (grantResults.size == 1 && grantResults[0] == PackageManager.PERMISSION_DENIED) { @@ -390,16 +417,6 @@ class MatterScannerFragment : Fragment() { } } - private fun showBluetoothEnableAlert() { - AlertDialog.Builder(requireContext()) - .setTitle(R.string.toast_bluetooth_not_enabled) - .setMessage(R.string.bluetooth_adapter_bar_turning_on) - .setPositiveButton(R.string.matter_camera_unavailable_alert_exit) { _, _ -> - requireActivity().finish() - } - .setCancelable(false) - .create().show() - } private fun showCameraPermissionAlert() { AlertDialog.Builder(requireContext()) @@ -407,9 +424,7 @@ class MatterScannerFragment : Fragment() { .setMessage(R.string.matter_camera_unavailable_alert_subtitle) .setPositiveButton(R.string.matter_camera_unavailable_alert_exit) { _, _ -> requireActivity().finish() - } - .setCancelable(false) - .create().show() + }.setCancelable(false).create().show() } private fun showLocationPermissionAlert() { @@ -418,24 +433,228 @@ class MatterScannerFragment : Fragment() { .setMessage(R.string.matter_location_unavailable_alert_subtitle) .setPositiveButton(R.string.matter_camera_unavailable_alert_exit) { _, _ -> requireActivity().finish() - } - .setCancelable(false) - .create().show() + }.setCancelable(false).create().show() } + @RequiresApi(Build.VERSION_CODES.R) + @Deprecated("Deprecated in Java") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) + + deviceInfo = CHIPDeviceInfo.fromSetupPayload( + payload, isShortDiscriminator + ) if (resultCode == WIFI_REQ_CODE) { println("Matter Wifi Selected") + typeNtw = INPUT_NETWORK_WIFI_TYPE_SELECTED networkMode = data!!.getStringExtra(ARG_PROVISION_NETWORK_TYPE).toString() - navigateToWiFiDevice(payload, isShortDiscriminator) + displayCommissioningDialog() + } else if (resultCode == THREAD_REQ_CODE) { println("Matter Thread Selected") + typeNtw = INPUT_NETWORK_THREAD_TYPE_SELECTED networkMode = data!!.getStringExtra(ARG_PROVISION_NETWORK_TYPE).toString() - navigateToThreadDevice(payload, isShortDiscriminator) + displayCommissioningDialog() + } else if (resultCode == CANCEL_REQ_CODE) { cancelNetworkInputDialog() startCamera() + } else if (resultCode == INPUT_WIFI_REQ_CODE) { + if (data != null) { + val wifiSSID = data.getStringExtra(WIFI_INPUT_SSID) + val wifiPassword = data.getStringExtra(WIFI_INPUT_PASSWORD) + + if (wifiSSID != null && wifiPassword != null) { + validateWiFiInput(wifiSSID, wifiPassword) + } + } + } else if (resultCode == Activity.RESULT_OK) { + if (data != null) { + val receiveOTBRInfo = data.getStringExtra(DIALOG_OTBR_INFO) + removeAlert() + validateThreadInput(receiveOTBRInfo!!.trim()) + } + } + } + + private fun displayCommissioningDialog() { + val str = requireContext().getString(R.string.matter_commissioning_device) + showMatterProgressDialog(str, false) + CoroutineScope(Dispatchers.IO).launch { + delay(COMMISSIONING_DIALOG_TIME_OUT) + removeProgress() + displayInputWindowBasedOnProvisionType() + } + } + + @SuppressLint("NewApi") + private fun validateThreadInput(inputOTBR: String) { + if (inputOTBR.isBlank()) { + Toast.makeText(requireContext(), "input OTBR is empty", Toast.LENGTH_SHORT).show() + return + } + val operationalDataset = dataFromHexString(inputOTBR.trim()) + + networkCredential = NetworkCredentialsParcelable.forThread( + NetworkCredentialsParcelable.ThreadCredentials(operationalDataset) + ) + startConnectingToDevice() + } + + @RequiresApi(Build.VERSION_CODES.R) + private fun validateWiFiInput(wifiSSID: String, wifiPassword: String) { + networkCredential = NetworkCredentialsParcelable.forWiFi( + NetworkCredentialsParcelable.WiFiCredentials( + wifiSSID, wifiPassword + ) + ) + startConnectingToDevice() + } + + + private fun dataFromHexString(string: String): ByteArray { + var modifiedString = string + if (string.length % 2 == 1) { + modifiedString = "0$string" + } + + val chars = modifiedString.toCharArray() + var i = 0 + val len = modifiedString.length + + + val data = ByteArray(len / 2) + val byteChars = CharArray(2) + + + while (i < len) { + byteChars[0] = chars[i++] + byteChars[1] = chars[i++] + var wholeByte = 0L + try { + wholeByte = byteChars.joinToString("").toLong(16) + + } catch (ex: NumberFormatException) { + var wholeByte = 0L + Timber.tag(TAG).e("Number Format Exception Occurred..") + } + + data[(i - 2) / 2] = wholeByte.toByte() + } + + return data + } + + @RequiresApi(Build.VERSION_CODES.R) + @SuppressLint("MissingPermission") + private fun startConnectingToDevice() { + if (gatt != null) { + return + } + scope.launch { + + bluetoothManager = BluetoothManager() + val strId = R.string.rendezvous_over_ble_scanning_text + val devInfo = deviceInfo.discriminator.toString() + showMessage(strId, devInfo) + showMatterProgressDialog( + getString(R.string.matter_network_selection_alert_title), true + ) + val device = bluetoothManager.getBluetoothDevice( + requireContext(), deviceInfo.discriminator, deviceInfo.isShortDiscriminator + ) ?: kotlin.run { + requireActivity().supportFragmentManager.popBackStack() + requireActivity().supportFragmentManager.popBackStack() + if (customProgressDialog != null) { + if (customProgressDialog!!.isShowing) { + customProgressDialog!!.dismiss() + } + } + showMessage(R.string.rendezvous_over_ble_scanning_failed_text) + return@launch + } + matterScanDevice = device + Timber.tag(TAG).e("Type : ${device.type}") + Timber.tag(TAG).e("UUID : ${device.uuids}") + Timber.tag(TAG).e("Name : ${device.name}") + Timber.tag(TAG).e("Address : ${device.address}") + Timber.tag(TAG).e("Alias : ${device.alias}") + if (customProgressDialog != null) { + if (customProgressDialog!!.isShowing) { + customProgressDialog!!.dismiss() + } + } + showMatterProgressDialog( + getString(R.string.matter_commissioning_message), true + ) + showMessage( + R.string.rendezvous_over_ble_connecting_text, + device.name ?: device.address.toString() + ) + removeAlert() + deviceId = DeviceIDUtil.getNextAvailableId(requireContext()) + gatt = bluetoothManager.connect(requireContext(), device) + deviceController.setCompletionListener(ConnectionCallback()) + val connId = bluetoothManager.connectionId + var network: NetworkCredentials? = null + + + val thread = networkCredential.threadCredentials + if (thread != null) { + network = NetworkCredentials.forThread( + NetworkCredentials.ThreadCredentials(thread.operationalDataset) + ) + } + val wifi = networkCredential.wiFiCredentials + if (wifi != null) { + network = NetworkCredentials.forWiFi( + NetworkCredentials.WiFiCredentials(wifi.ssid, wifi.password) + ) + } + + setAttestationDelegate() + + deviceController.pairDevice( + gatt, connId, deviceId, deviceInfo.setupPinCode, network + ) + DeviceIDUtil.setNextAvailableId(requireContext(), deviceId + 1) + } + } + + private fun setAttestationDelegate() { + deviceController.setDeviceAttestationDelegate(DEVICE_ATTESTATION_FAILED_TIMEOUT) { devicePtr, _, errorCode -> + Timber.tag(TAG).e( + "Device attestation errorCode: $errorCode, \nLook at 'src/credentials/attestation_verifier/DeviceAttestationVerifier.h' \nAttestationVerificationResult enum to understand the errors" + ) + + val activity = requireActivity() + Timber.tag(TAG).e("setAttestationDelegate()--errorCode: $errorCode") + if (errorCode == STATUS_PAIRING_SUCCESS) { + Timber.tag(TAG).e("setAttestationDelegate() In--errorCode: $errorCode") + activity.runOnUiThread { + deviceController.continueCommissioning(devicePtr, true) + } + + return@setDeviceAttestationDelegate + } + activity.runOnUiThread(Runnable { + if (alertDialog != null && alertDialog?.isShowing == true) { + Timber.tag(TAG).e("Dialog is already showing...") + return@Runnable + } + alertDialog = android.app.AlertDialog.Builder(activity).setPositiveButton( + "Continue" + ) { alertDialog, id -> + deviceController.continueCommissioning(devicePtr, true) + }.setNegativeButton( + "No" + ) { alertDialog, id -> + deviceController.continueCommissioning(devicePtr, false) + }.setTitle("Device Attestation") + .setMessage("Device Attestation failed for device under commissioning. Do you wish to continue pairing?") + .show() + }) + } } @@ -445,6 +664,12 @@ class MatterScannerFragment : Fragment() { } } + private fun removeAlert() { + if (otbrEntryDialog != null) { + otbrEntryDialog!!.dismiss() + } + } + override fun onDestroyView() { super.onDestroyView() cancelNetworkInputDialog() @@ -456,33 +681,40 @@ class MatterScannerFragment : Fragment() { } } + private fun showMessage(msgResId: Int, stringArgs: String? = null) { + requireActivity().runOnUiThread { + val context = requireContext() + val msg = context.getString(msgResId, stringArgs) + Timber.tag(TAG).e("showMessage:$msg") + Toast.makeText(context, msg, Toast.LENGTH_SHORT).show() + } + } + private fun showDeviceInfoDialog(message: String) { try { - if (isAdded && requireActivity() != null && !requireActivity().isFinishing) { + if (isAdded && !requireActivity().isFinishing) { requireActivity().runOnUiThread { - if (!deviceInfodialogShown && (deviceDialog == null || !deviceDialog!!.isShowing())) { - deviceInfodialogShown=true + if (!deviceInfoDialogShown && (deviceDialog == null || !deviceDialog!!.isShowing())) { + deviceInfoDialogShown = true deviceDialog = DialogDeviceInfoFragment.Builder() - .setTitle(getString(R.string.qr_code_info)) - .setMessage(message) + .setTitle(getString(R.string.qr_code_info)).setMessage(message) .setPositiveButton(getString(R.string.start_commissioning)) { dialog, which -> // Positive button click logic networkSelectionDialog = MatterNetworkSelectionInputDialogFragment.newInstance() - networkSelectionDialog!!.setTargetFragment(this, DIALOG_NTW_MODE_FRAGMENT) + networkSelectionDialog!!.setTargetFragment( + this, DIALOG_NTW_MODE_FRAGMENT + ) networkSelectionDialog!!.show( - requireActivity().supportFragmentManager, ALERT_NTW_MODE_DIALOG_TAG + requireActivity().supportFragmentManager, + ALERT_NTW_MODE_DIALOG_TAG ) - deviceDialog?.isShowing()==false - deviceInfodialogShown=false - } - .setNegativeButton("Cancel") { dialog, which -> + deviceInfoDialogShown = false + }.setNegativeButton("Cancel") { dialog, which -> deviceDialog?.dismiss() - deviceDialog?.isShowing()==false - deviceInfodialogShown=false - } - .build() + deviceInfoDialogShown = false + }.build() deviceDialog?.show(parentFragmentManager, "DialogDeviceInfoFragment") } @@ -495,30 +727,306 @@ class MatterScannerFragment : Fragment() { } } - interface CallBack { - fun onChipDeviceInfoReceived(deviceInfo: CHIPDeviceInfo, ntwInputType: String) + private fun displayInputWindowBasedOnProvisionType() { + when (typeNtw) { + INPUT_NETWORK_WIFI_TYPE_SELECTED /* ProvisionNetworkType.WIFI */ -> { + if (wifiEntryDialog == null) { + val prev = requireActivity().supportFragmentManager.findFragmentByTag( + DIALOG_WIFI_INPUT_TAG + ) + if (prev == null) { + wifiEntryDialog = MatterWifiInputDialogFragment.newInstance() + wifiEntryDialog!!.setTargetFragment(this, DIALOG_WIFI_FRAGMENT) + wifiEntryDialog!!.show( + requireActivity().supportFragmentManager, DIALOG_WIFI_INPUT_TAG + ) + } + } + } + + INPUT_NETWORK_THREAD_TYPE_SELECTED /* ProvisionNetworkType.THREAD*/ -> { + if (otbrEntryDialog == null) { + val prev = requireActivity().supportFragmentManager.findFragmentByTag( + DIALOG_THREAD_INPUT_TAG + ) + if (prev == null) { + otbrEntryDialog = MatterOTBRInputDialogFragment.newInstance() + + otbrEntryDialog!!.setTargetFragment(this, DIALOG_THREAD_FRAGMENT) + otbrEntryDialog!!.show( + requireActivity().supportFragmentManager, DIALOG_THREAD_INPUT_TAG + ) + } + } + } + + else -> { + println("Unhandled....") + } + + } + } + + private fun showMatterProgressDialog(message: String, showCancelButton: Boolean) { + customProgressDialog = CustomProgressDialog(requireContext()) + customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + customProgressDialog!!.setMessage(message) + customProgressDialog!!.setCanceledOnTouchOutside(false) + customProgressDialog!!.setCustomButtonVisible(showCancelButton) { + deviceController.close() + requireActivity().supportFragmentManager.popBackStack( + null, FragmentManager.POP_BACK_STACK_INCLUSIVE + ) + sendToScanResultFragment() + + customProgressDialog!!.dismiss() + } + customProgressDialog!!.show() + } + + private fun sendToScanResultFragment() { + val bundle = Bundle() + scannedDeviceList = SharedPrefsUtils.retrieveSavedDevices(mPrefs) + bundle.putParcelableArrayList(ARG_DEVICE_LIST, scannedDeviceList) + + + val fragment = MatterScannedResultFragment.newInstance() + fragment.arguments = bundle + + parentFragmentManager.beginTransaction().replace( + R.id.matter_container, fragment + ) // R.id.matterContainer should be the container in the activity or fragment layout where the new fragment will be placed + .addToBackStack(null) // Add to back stack if you want the user to be able to navigate back + .commit() + } + + private fun removeProgress() { + if (customProgressDialog?.isShowing == true) { + customProgressDialog?.dismiss() + } + } + + private fun onCommissionCompleted() { + try { + ChipClient.getDeviceController(requireContext()).close() + } catch (e: Exception) { + Timber.tag(TAG).e("onCommissionCompleted error $e") + } + } + + inner class ConnectionCallback : GenericChipDeviceListener() { + override fun onConnectDeviceComplete() { + super.onConnectDeviceComplete() + Timber.tag(TAG).e("onConnectDeviceComplete") + } + + override fun onStatusUpdate(status: Int) { + super.onStatusUpdate(status) + Timber.tag(TAG).e("onStatusUpdate : $status.toString()") + } + + private suspend fun getDescriptorClusterForDevice(): ChipClusters.DescriptorCluster { + return ChipClusters.DescriptorCluster( + + ChipClient.getConnectedDevicePointer(requireContext(), deviceId), + ON_OFF_CLUSTER_ENDPOINT + ) + } + + @SuppressLint("MissingPermission") + override fun onCommissioningComplete(nodeId: Long, errorCode: Long) { + super.onCommissioningComplete(nodeId, errorCode) + Timber.tag(TAG).e("onCommissioningComplete : NodeID: $nodeId.toString()") + Timber.tag(TAG).e("%s%s", "onCommissioningComplete : errorCode: ", errorCode.toString()) + removeAlert() + if (errorCode == STATUS_PAIRING_SUCCESS) { + if (customProgressDialog?.isShowing == true) { + customProgressDialog?.dismiss() + } + Timber.tag(TAG).e("pairing success") + onCommissionCompleted() + + scope.launch { + getDescriptorClusterForDevice().readDeviceTypeListAttribute(object : + ChipClusters.DescriptorCluster.DeviceTypeListAttributeCallback { + override fun onError(error: java.lang.Exception?) { + Timber.tag(TAG).e("MatterConnect Error: $error") + } + + override fun onSuccess(valueList: MutableList?) { + + Timber.tag(TAG) + .e("deviceType $ valueList?.get(0)?.deviceType?.toInt()!!") + val deviceType = valueList?.get(0)?.deviceType?.toInt()!! + println("device Type: $deviceType}") + println("device Info: ${matterScanDevice.name} DeviceId: ${deviceId}") + var device = "" + + when (deviceType) { + DOOR_LOCK_TYPE -> device = + requireContext().getString(R.string.matter_lock_list) + + DIMMABLE_PLUG_IN_UNIT_TYPE -> device = + requireContext().getString(R.string.matter_plug_list) + + OCCUPANCY_SENSOR_TYPE -> device = + requireContext().getString(R.string.matter_occupancy_sensor_list) - fun setNetworkType(type: ProvisionNetworkType) + TEMPERATURE_SENSOR_TYPE -> device = + requireContext().getString(R.string.matter_temperature_sensor_list) + + CONTACT_SENSOR_TYPE -> device = + requireContext().getString(R.string.matter_contact_sensor_list) + + THERMOSTAT_TYPE -> device = + requireContext().getString(R.string.matter_thermostat_list) + + DIMMABLE_LIGHT_TYPE, ENHANCED_COLOR_LIGHT_TYPE, ON_OFF_LIGHT_TYPE, COLOR_TEMPERATURE_LIGHT_TYPE -> device = + requireContext().getString(R.string.matter_light_list) + + WINDOW_COVERING_TYPE -> device = + requireContext().getString(R.string.matter_window_list) + + DISHWASHER_TYPE -> device = + requireContext().getString(R.string.matter_dishwahser_list) + + else -> device = matterScanDevice.name + + + // else -> device = matterScanDevice.name + } + + val deviceName = device + COLON_WITH_SPACE + deviceId + println("device Info: ${deviceName} DeviceId: ${deviceId}") + + showEditDeviceNameDialog(device, valueList) + + + } + + private fun showEditDeviceNameDialog( + device: String, + valueList: MutableList + ) { + + val customInputDialog = CustomInputDialog.newInstance( + requireContext(), + device, + getString(R.string.add_device_name), + getString( + R.string.add_device_subtitle + ) + ) + customInputDialog.setOnButtonClickListener { deviceName -> + + val matterInfo = MatterScannedResultModel( + deviceName, + matterScanDevice.address, + matterScanDevice.type, + deviceId, + valueList.get(0).deviceType?.toInt()!!, + isDeviceOnline = true + ) + FragmentUtils.getHost( + this@MatterScannerFragment, CallBack::class.java + ).onCommissionCompleteLoadData(matterInfo) + } + + customInputDialog.show(requireFragmentManager(), "CustomInputDialog") + + + } + + }) + } + + + } else { + if (customProgressDialog?.isShowing() == true) { + customProgressDialog?.dismiss() + } + requireActivity().runOnUiThread { + showAlertWindow(errorCode) + } + } + onCommissionCompleted() + } + } + + private fun showAlertWindow(errorCode: Long) { + val builder = android.app.AlertDialog.Builder(context, R.style.AlertDialogTheme) + val alertMessageStart = + requireContext().getString(R.string.matter_device_commissioning_failed) + val alertTitle = requireContext().getString(R.string.matter_delete_alert_title) + + + builder.setTitle(alertTitle) + builder.setMessage(alertMessageStart) + builder.setPositiveButton( + requireContext().getString(R.string.matter_alert_ok) + ) { dialog: DialogInterface?, which: Int -> + // When the user click yes button then app will close + dialog?.dismiss() + requireActivity().supportFragmentManager.popBackStack() + } + builder.setNegativeButton( + requireContext().getString(R.string.matter_cancel) + ) { dialog: DialogInterface?, which: Int -> + // When the user click yes button then app will close + dialog?.dismiss() + requireActivity().supportFragmentManager.popBackStack() + } + builder.show() + + } + + override fun onDestroy() { + super.onDestroy() + stopCamera() + } + + + interface CallBack { + fun onCommissionCompleteLoadData( + matterScannedResultModel: MatterScannedResultModel + ) } companion object { private val TAG = MatterScannerFragment::class.java.classes.toString() + private const val COMMISSIONING_DIALOG_TIME_OUT = 1000L + const val CANCEL_REQ_CODE = 5000 + const val WIFI_REQ_CODE = 5001 + const val THREAD_REQ_CODE = 5002 + const val INPUT_WIFI_REQ_CODE = 5003 + private const val DEVICE_ATTESTATION_FAILED_TIMEOUT = 600 + private const val DIALOG_THREAD_FRAGMENT = 999 + private const val DIALOG_WIFI_FRAGMENT = 997 + + private const val STATUS_PAIRING_SUCCESS = 0L + private const val REQUEST_CODE_LOCATION_PERMISSION = 101 private const val REQUEST_CODE_CAMERA_PERMISSION = 100 private const val RATIO_4_3_VALUE = 4.0 / 3.0 private const val RATIO_16_9_VALUE = 16.0 / 9.0 private const val DIALOG_NTW_MODE_FRAGMENT = 998 - const val CANCEL_REQ_CODE = 5000 - const val WIFI_REQ_CODE = 5001 - const val THREAD_REQ_CODE = 5002 - const val INPUT_WIFI_REQ_CODE = 5003 + + const val SPACE = " " const val INPUT_NETWORK_WIFI_TYPE_SELECTED = "wifi" const val INPUT_NETWORK_THREAD_TYPE_SELECTED = "thread" const val WIFI_INPUT_SSID = "wifi_input_ssid" const val WIFI_INPUT_PASSWORD = "wifi_input_password" const val ARG_PROVISION_NETWORK_TYPE = "dialog_network_mode_info" + const val DIALOG_OTBR_INFO = "Dialog_OTBR_Info" + const val DIA_INPUT_NTW_TYPE = "dia_input_ntw_type" + + private const val COLON_WITH_SPACE = " - " private const val ALERT_NTW_MODE_DIALOG_TAG = "alert_ntw_mode_dialog_tag" + private const val DIALOG_THREAD_INPUT_TAG = "MatterThreadDialogFragmentTAG" + private const val DIALOG_WIFI_INPUT_TAG = "MatterWiFiDialogFragmentTAG" + private const val ARG_DEVICE_LIST = "device_list" + fun newInstance(): MatterScannerFragment { val args = Bundle() diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterTemperatureSensorFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterTemperatureSensorFragment.kt index 4bf3fa9b..38c5b8d0 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterTemperatureSensorFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterTemperatureSensorFragment.kt @@ -27,6 +27,7 @@ import com.siliconlabs.bledemo.features.demo.matter_demo.controller.GenericChipD import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ARG_DEVICE_MODEL import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.INIT import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ON_OFF_CLUSTER_ENDPOINT +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannerFragment.Companion.SPACE import com.siliconlabs.bledemo.features.demo.matter_demo.model.MatterScannedResultModel import com.siliconlabs.bledemo.features.demo.matter_demo.utils.ChipClient import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomProgressDialog @@ -60,7 +61,7 @@ class MatterTemperatureSensorFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mPrefs = requireContext().getSharedPreferences( - "your_preference_name", + MatterDemoActivity.MATTER_PREF, AppCompatActivity.MODE_PRIVATE ) model = requireArguments().getParcelable(ARG_DEVICE_MODEL)!! @@ -68,14 +69,13 @@ class MatterTemperatureSensorFragment : Fragment() { if (deviceId != null) { - showMatterProgressDialog(getString(R.string.please_wait)) + showMatterProgressDialog(getString(R.string.matter_device_status)) - // retrieveSavedDevices() - GlobalScope.launch { + CoroutineScope(Dispatchers.IO).launch { // This code will run asynchronously - val resultq = checkForDeviceStatus() - if (resultq) { + val resInfo = checkForDeviceStatus() + if (resInfo) { println("Operation was successful") removeProgress() } @@ -114,17 +114,10 @@ class MatterTemperatureSensorFragment : Fragment() { } } - override fun onAttach(@NotNull context: Context) { - super.onAttach(context) - } - - override fun onDetach() { - super.onDetach() - } private fun showMessageDialog() { try { - if (isAdded() && requireActivity() != null && !requireActivity().isFinishing) { + if (isAdded && !requireActivity().isFinishing) { requireActivity().runOnUiThread { if (!MessageDialogFragment.isDialogShowing()) { dialog = MessageDialogFragment() @@ -137,8 +130,7 @@ class MatterTemperatureSensorFragment : Fragment() { FragmentUtils.getHost( this@MatterTemperatureSensorFragment, CallBackHandler::class.java - ) - .onBackHandler() + ).onBackHandler() } } val transaction: FragmentTransaction = @@ -151,14 +143,14 @@ class MatterTemperatureSensorFragment : Fragment() { Timber.e("device offline") } } catch (e: Exception) { - Timber.e("" + e) + Timber.e("Exception Occurred $e") } } private fun removeProgress() { - if (customProgressDialog?.isShowing() == true) { + if (customProgressDialog?.isShowing == true) { customProgressDialog?.dismiss() } } @@ -167,6 +159,7 @@ class MatterTemperatureSensorFragment : Fragment() { customProgressDialog = CustomProgressDialog(requireContext()) customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) customProgressDialog!!.setMessage(message) + customProgressDialog!!.setCanceledOnTouchOutside(false) customProgressDialog!!.show() } @@ -174,7 +167,7 @@ class MatterTemperatureSensorFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { binding = FragmentMatterTemperatureSensorBinding.inflate(inflater, container, false) return binding.root @@ -195,8 +188,8 @@ class MatterTemperatureSensorFragment : Fragment() { private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - if (requireActivity().supportFragmentManager.getBackStackEntryCount() > 0) { - requireActivity().supportFragmentManager.popBackStack(); + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + requireActivity().supportFragmentManager.popBackStack() } else { FragmentUtils.getHost( this@MatterTemperatureSensorFragment, @@ -287,7 +280,7 @@ class MatterTemperatureSensorFragment : Fragment() { } override fun onError(ex: Exception?) { - Timber.e(TAG, "read local temp command failure", ex) + Timber.e(TAG, "read local temp command failure :$ex") // removeProgress() SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) showMessageDialog() @@ -301,16 +294,16 @@ class MatterTemperatureSensorFragment : Fragment() { @SuppressLint("SetTextI18n") override fun onSuccess(value: Int?) { // removeProgress() - requireActivity().runOnUiThread(Runnable { + requireActivity().runOnUiThread { val resValue = addDecimalPointToInteger(value) binding.txtValue.text = SPACE + (resValue) + TEMPERATURE_UNIT - }) + } } - override fun onError(ex: java.lang.Exception?) { + override fun onError(exe: java.lang.Exception?) { SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) - Timber.tag(TAG).e( "read local temp command failure"+ ex) + Timber.tag(TAG).e("read local temp command failure :$exe") showMessageDialog() } @@ -330,7 +323,7 @@ class MatterTemperatureSensorFragment : Fragment() { inner class TempSensorChipControllerCallback : GenericChipDeviceListener() { override fun onConnectDeviceComplete() {} - override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { + override fun onCommissioningComplete(nodeId: Long, errorCode: Long) { Timber.d(TAG, "onCommissioningComplete for nodeId $nodeId: $errorCode") //showMessage("Address update complete for nodeId $nodeId with code $errorCode") } @@ -343,7 +336,7 @@ class MatterTemperatureSensorFragment : Fragment() { Timber.d(TAG, "onCloseBleComplete") } - override fun onError(error: Throwable) { + override fun onError(error: Throwable?) { super.onError(error) Timber.d(TAG, "onError : $error") } @@ -357,9 +350,8 @@ class MatterTemperatureSensorFragment : Fragment() { const val MIN_REFRESH_PERIOD_S = 2 const val MAX_REFRESH_PERIOD_S = 10 const val TEMPERATURE_UNIT = " \u2103" - const val SPACE = "" - private val TAG = MatterTemperatureSensorFragment.javaClass.simpleName.toString() + private val TAG = Companion::class.java.simpleName.toString() fun newInstance(): MatterTemperatureSensorFragment = MatterTemperatureSensorFragment() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterThermostatFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterThermostatFragment.kt index 4e365b33..b75ad6e7 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterThermostatFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterThermostatFragment.kt @@ -1,11 +1,10 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.fragments -import android.content.Context +import android.annotation.SuppressLint import android.content.SharedPreferences import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -23,13 +22,11 @@ import com.siliconlabs.bledemo.bluetooth.beacon_utils.eddystone.Constants import com.siliconlabs.bledemo.databinding.FragmentMatterThermostatBinding import com.siliconlabs.bledemo.features.demo.matter_demo.activities.MatterDemoActivity import com.siliconlabs.bledemo.features.demo.matter_demo.controller.GenericChipDeviceListener -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterConnectFragment.Companion.SPACE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ARG_DEVICE_INFO import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ARG_DEVICE_MODEL import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.INIT import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ON_OFF_CLUSTER_ENDPOINT +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannerFragment.Companion.SPACE import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterTemperatureSensorFragment.Companion.TEMPERATURE_UNIT -import com.siliconlabs.bledemo.features.demo.matter_demo.model.CHIPDeviceInfo import com.siliconlabs.bledemo.features.demo.matter_demo.model.MatterScannedResultModel import com.siliconlabs.bledemo.features.demo.matter_demo.utils.ChipClient import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomProgressDialog @@ -38,13 +35,10 @@ import com.siliconlabs.bledemo.features.demo.matter_demo.utils.MessageDialogFrag import com.siliconlabs.bledemo.features.demo.matter_demo.utils.SharedPrefsUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.jetbrains.annotations.NotNull import timber.log.Timber -import java.text.DecimalFormat class MatterThermostatFragment : Fragment() { @@ -63,28 +57,26 @@ class MatterThermostatFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mPrefs = requireContext().getSharedPreferences( - "your_preference_name", + MatterDemoActivity.MATTER_PREF, AppCompatActivity.MODE_PRIVATE ) if (requireArguments() != null) { model = requireArguments().getParcelable(ARG_DEVICE_MODEL)!! deviceId = model.deviceId - Timber.tag(TAG).e( "deviceID: " + model) + Timber.tag(TAG).e("deviceID: $model") } if (deviceId != null) { - showMatterProgressDialog(getString(R.string.please_wait)) + showMatterProgressDialog(getString(R.string.matter_device_status)) // retrieveSavedDevices() - GlobalScope.launch { + CoroutineScope(Dispatchers.IO).launch { // This code will run asynchronously - val resultq = checkForDeviceStatus() - if (resultq) { + val resInfo = checkForDeviceStatus() + if (resInfo) { println("Operation was successful") removeProgress() - // prepareList() - } } } @@ -118,30 +110,23 @@ class MatterThermostatFragment : Fragment() { } } - override fun onAttach(@NotNull context: Context) { - super.onAttach(context) - } - override fun onDetach() { - super.onDetach() - } private fun showMessageDialog() { try { - if (isAdded() && requireActivity()!=null && !requireActivity().isFinishing){ + if (isAdded && !requireActivity().isFinishing) { requireActivity().runOnUiThread { if (!MessageDialogFragment.isDialogShowing()) { dialog = MessageDialogFragment() dialog.setMessage(getString(R.string.matter_device_offline_text)) dialog.setOnDismissListener { removeProgress() - if (requireActivity().supportFragmentManager.getBackStackEntryCount() > 0) { - requireActivity().supportFragmentManager.popBackStack(); + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + requireActivity().supportFragmentManager.popBackStack() } else { FragmentUtils.getHost( this@MatterThermostatFragment, MatterDoorFragment.CallBackHandler::class.java - ) - .onBackHandler() + ).onBackHandler() } } val transaction: FragmentTransaction = @@ -150,18 +135,18 @@ class MatterThermostatFragment : Fragment() { dialog.show(transaction, dialogTag) } } - }else{ + } else { Timber.e("device offline") } - }catch (e:Exception){ - Timber.e("device offline"+e) + } catch (e: Exception) { + Timber.e("device offline $e") } } private fun removeProgress() { - if (customProgressDialog?.isShowing() == true) { + if (customProgressDialog?.isShowing == true) { customProgressDialog?.dismiss() } } @@ -170,6 +155,7 @@ class MatterThermostatFragment : Fragment() { customProgressDialog = CustomProgressDialog(requireContext()) customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) customProgressDialog!!.setMessage(message) + customProgressDialog!!.setCanceledOnTouchOutside(false) customProgressDialog!!.show() } @@ -189,8 +175,8 @@ class MatterThermostatFragment : Fragment() { scope = viewLifecycleOwner.lifecycleScope deviceController.setCompletionListener(DoorChipControllerCallback()) - binding.btnReadValue.setLongClickable(false); - binding.btnReadValue.setText(requireContext().getText(R.string.matter_temp_refresh)) + binding.btnReadValue.isLongClickable = false + binding.btnReadValue.text = requireContext().getText(R.string.matter_temp_refresh) binding.btnReadValue.setOnClickListener { scope.launch { sendReadValueCommandClick() } @@ -200,8 +186,8 @@ class MatterThermostatFragment : Fragment() { private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - if (requireActivity().supportFragmentManager.getBackStackEntryCount() > 0) { - requireActivity().supportFragmentManager.popBackStack(); + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + requireActivity().supportFragmentManager.popBackStack() } else { FragmentUtils.getHost(this@MatterThermostatFragment, CallBackHandler::class.java) .onBackHandler() @@ -242,26 +228,26 @@ class MatterThermostatFragment : Fragment() { private suspend fun sendReadValueCommandClick() { getThermostatClusterForDevice().readLocalTemperatureAttribute(object : ChipClusters.ThermostatCluster.LocalTemperatureAttributeCallback { + @SuppressLint("SetTextI18n") override fun onSuccess(value: Int?) { - SharedPrefsUtils.updateDeviceByDeviceId(mPrefs,deviceId,true) - requireActivity().runOnUiThread(Runnable { + SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) + requireActivity().runOnUiThread { val resValue = roundDoubleToInt(addDecimalPointToInteger(value)) - Log.e( - TAG, - "read local temp command Success: " + addDecimalPointToInteger(value) + Timber.tag(TAG).e( + "read local temp command Success: ${addDecimalPointToInteger(value)}" ) binding.txtValue.setText(SPACE + (resValue) + TEMPERATURE_UNIT) - }) + } } - override fun onError(ex: Exception?) { - // removeProgress() + override fun onError(exe: Exception?) { + // removeProgress() SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) showMessageDialog() - Timber.tag(TAG).e( "read local temp command failure", ex) + Timber.tag(TAG).e("read local temp command failure:$exe") } }) } @@ -277,7 +263,7 @@ class MatterThermostatFragment : Fragment() { inner class DoorChipControllerCallback : GenericChipDeviceListener() { override fun onConnectDeviceComplete() {} - override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { + override fun onCommissioningComplete(nodeId: Long, errorCode: Long) { Timber.d(TAG, "onCommissioningComplete for nodeId $nodeId: $errorCode") //showMessage("Address update complete for nodeId $nodeId with code $errorCode") } @@ -290,7 +276,7 @@ class MatterThermostatFragment : Fragment() { Timber.d(TAG, "onCloseBleComplete") } - override fun onError(error: Throwable) { + override fun onError(error: Throwable?) { super.onError(error) Timber.d(TAG, "onError : $error") } @@ -301,7 +287,7 @@ class MatterThermostatFragment : Fragment() { } companion object { - private val TAG = MatterThermostatFragment.javaClass.simpleName.toString() + private val TAG = Companion::class.java.simpleName.toString() fun newInstance(): MatterThermostatFragment = MatterThermostatFragment() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterWifiInputDialogFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterWifiInputDialogFragment.kt index 8bc81c87..2105a212 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterWifiInputDialogFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterWifiInputDialogFragment.kt @@ -11,7 +11,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.Window -import android.view.WindowManager import android.widget.Toast import androidx.fragment.app.DialogFragment import com.siliconlabs.bledemo.R @@ -32,7 +31,7 @@ class MatterWifiInputDialogFragment : DialogFragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { if (!flag) { flag = true binding = DialogConfigureWifiMatterBinding.inflate(inflater, container, false) @@ -40,6 +39,7 @@ class MatterWifiInputDialogFragment : DialogFragment() { if (dialog != null && dialog!!.window != null) { dialog!!.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) dialog!!.window!!.requestFeature(Window.FEATURE_NO_TITLE) + dialog!!.setCanceledOnTouchOutside(false) } return binding.root } @@ -65,15 +65,19 @@ class MatterWifiInputDialogFragment : DialogFragment() { binding.positiveBtn.setOnClickListener { val wifiSSID = binding.editNetworkSSID.text.toString().trim() val wifiPassword = binding.editWiFiPassword.text.toString().trim() - if (wifiSSID.isNullOrBlank() || wifiPassword.isNullOrBlank()) { - Toast.makeText(requireContext(), - getString(R.string.ssid_and_password_required), Toast.LENGTH_SHORT) + if (wifiSSID.isBlank() || wifiPassword.isBlank()) { + Toast.makeText( + requireContext(), + getString(R.string.ssid_and_password_required), Toast.LENGTH_SHORT + ) .show() return@setOnClickListener } if (!FragmentUtils.isPasswordValid(wifiPassword)) { - Toast.makeText(requireContext(), - getString(R.string.password_must_be_min_8_characters), Toast.LENGTH_SHORT).show() + Toast.makeText( + requireContext(), + getString(R.string.password_must_be_min_8_characters), Toast.LENGTH_SHORT + ).show() return@setOnClickListener } val intent = Intent() @@ -103,9 +107,11 @@ class MatterWifiInputDialogFragment : DialogFragment() { } } } + interface CallBackHandler { fun onBackHandler() } + private fun getScreenWidth(activity: Activity): Int { val size = Point() activity.windowManager.defaultDisplay.getSize(size) @@ -120,4 +126,5 @@ class MatterWifiInputDialogFragment : DialogFragment() { super.onDestroy() flag = false } + } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterWindowCoverFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterWindowCoverFragment.kt index aacef3b9..53cf8eae 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterWindowCoverFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/fragments/MatterWindowCoverFragment.kt @@ -1,11 +1,9 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.fragments -import android.content.Context import android.content.SharedPreferences import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -16,7 +14,6 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentTransaction import androidx.lifecycle.lifecycleScope import chip.devicecontroller.ChipClusters -import chip.devicecontroller.ChipClusters.IntegerAttributeCallback import chip.devicecontroller.ChipDeviceController import chip.devicecontroller.GetConnectedDeviceCallbackJni import com.siliconlabs.bledemo.R @@ -26,6 +23,7 @@ import com.siliconlabs.bledemo.features.demo.matter_demo.activities.MatterDemoAc import com.siliconlabs.bledemo.features.demo.matter_demo.controller.GenericChipDeviceListener import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ARG_DEVICE_MODEL import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.ON_OFF_CLUSTER_ENDPOINT +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterLightFragment.Companion.TIME_OUT import com.siliconlabs.bledemo.features.demo.matter_demo.model.MatterScannedResultModel import com.siliconlabs.bledemo.features.demo.matter_demo.utils.ChipClient import com.siliconlabs.bledemo.features.demo.matter_demo.utils.CustomProgressDialog @@ -34,11 +32,9 @@ import com.siliconlabs.bledemo.features.demo.matter_demo.utils.MessageDialogFrag import com.siliconlabs.bledemo.features.demo.matter_demo.utils.SharedPrefsUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.jetbrains.annotations.NotNull import timber.log.Timber @@ -55,10 +51,11 @@ class MatterWindowCoverFragment : Fragment() { private var endpointId: Int = ON_OFF_CLUSTER_ENDPOINT private lateinit var model: MatterScannedResultModel private lateinit var binding: FragmentMatterWindowCoverBinding + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mPrefs = requireContext().getSharedPreferences( - "your_preference_name", + MatterDemoActivity.MATTER_PREF, AppCompatActivity.MODE_PRIVATE ) if (requireArguments() != null) { @@ -72,9 +69,7 @@ class MatterWindowCoverFragment : Fragment() { showMatterProgressDialog(getString(R.string.matter_device_status)) // retrieveSavedDevices() - GlobalScope.launch { - // This code will run asynchronously - + CoroutineScope(Dispatchers.IO).launch { val resultq = checkForDeviceStatus() if (resultq) { removeProgress() @@ -88,36 +83,35 @@ class MatterWindowCoverFragment : Fragment() { return withContext(Dispatchers.Default) { // Simulate a time-consuming task - deviceController.getConnectedDevicePointer(deviceId, object : - GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback { - override fun onDeviceConnected(devicePointer: Long) { - Log.e(TAG, "devicePointer " + devicePointer + " nodeid " + deviceId) - model.isDeviceOnline = true - SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) - // removeProgress() + deviceController.getConnectedDevicePointer(deviceId, + object : GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback { + override fun onDeviceConnected(devicePointer: Long) { + Timber.tag(TAG) + .e("devicePointer :$devicePointer + nodeid :$deviceId") + model.isDeviceOnline = true + SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) + // removeProgress() - } + } - override fun onConnectionFailure(nodeId: Long, error: Exception?) { - model.isDeviceOnline = false - removeProgress() - SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) + override fun onConnectionFailure(nodeId: Long, error: Exception?) { + model.isDeviceOnline = false + removeProgress() + SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) - showMessageDialog(getString(R.string.matter_device_offline_text)) - } - }) + showMessageDialog(getString(R.string.matter_device_offline_text)) + } + }) delay(Constants.SCAN_TIMER * 500) - //handleSubscriptionForWindowCover() - // Return the result (true or false based on the actual result) true } } private suspend fun handleSubscriptionForWindowCover() { getWindowClusterForDevice().subscribeModeAttribute( - object : IntegerAttributeCallback { + object : ChipClusters.IntegerAttributeCallback { override fun onSuccess(value: Int) { println("value $value") } @@ -132,7 +126,7 @@ class MatterWindowCoverFragment : Fragment() { } private fun showMessageDialog(msg: String) { - if (isAdded() && requireActivity() != null && !requireActivity().isFinishing) { + if (isAdded && !requireActivity().isFinishing) { requireActivity().runOnUiThread { if (!MessageDialogFragment.isDialogShowing()) { dialog = MessageDialogFragment() @@ -140,14 +134,13 @@ class MatterWindowCoverFragment : Fragment() { dialog.setOnDismissListener { removeProgress() - if (requireActivity().supportFragmentManager.getBackStackEntryCount() > 0) { - requireActivity().supportFragmentManager.popBackStack(); + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + requireActivity().supportFragmentManager.popBackStack() } else { FragmentUtils.getHost( this@MatterWindowCoverFragment, CallBackHandler::class.java - ) - .onBackHandler() + ).onBackHandler() } } val transaction: FragmentTransaction = @@ -172,13 +165,14 @@ class MatterWindowCoverFragment : Fragment() { customProgressDialog = CustomProgressDialog(requireContext()) customProgressDialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) customProgressDialog!!.setMessage(message) + customProgressDialog!!.setCanceledOnTouchOutside(false) customProgressDialog!!.show() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { // Inflate the layout for this fragment binding = FragmentMatterWindowCoverBinding.inflate(inflater, container, false) return binding.root @@ -189,36 +183,166 @@ class MatterWindowCoverFragment : Fragment() { super.onViewCreated(view, savedInstanceState) scope = viewLifecycleOwner.lifecycleScope deviceController.setCompletionListener(WindowCoverChipControllerCallback()) - binding.btnMatterWindowState.setImageResource(R.drawable.window_close) - binding.btnWindowClose.isLongClickable = false; - binding.btnWindowOpen.isLongClickable = false; + scope.launch { + readTargetPositionLift() + readTargetPositionTilt() + } + binding.btnMatterWindowState.setImageResource(R.drawable.w0) + binding.overlayImage.setImageResource(R.drawable.w0) + + binding.btnWindowClose.isLongClickable = false + binding.btnWindowOpen.isLongClickable = false binding.btnWindowClose.text = requireContext().getText(R.string.matter_window_close) binding.btnWindowOpen.text = requireContext().getText(R.string.matter_window_open) + binding.btnWindowLift.isLongClickable = false + binding.btnWindowLift.isLongClickable = false + binding.btnWindowLift.text = requireContext().getText(R.string.lift) + + binding.btnWindowTilt.isLongClickable = false + binding.btnWindowTilt.isLongClickable = false + binding.btnWindowTilt.text = requireContext().getText(R.string.tilt) + binding.btnWindowOpen.setOnClickListener { + showMatterProgressDialog(getString(R.string.matter_door_lock_in_progress)) scope.launch { sendWindowOpenCommandClick() } } binding.btnWindowClose.setOnClickListener { + showMatterProgressDialog(getString(R.string.matter_door_lock_in_progress)) scope.launch { sendWindowCloseCommandClick() } } + + binding.btnWindowLift.setOnClickListener { + showMatterProgressDialog(getString(R.string.matter_door_lock_in_progress)) + scope.launch { + if (!binding.etLift.text.isNullOrEmpty()) { + var liftPtage = binding.etLift.text.toString().toIntOrNull() + if (liftPtage != null && liftPtage in 1..100) { + liftPtage = 100 - liftPtage + sendWindowLiftCommandClick(liftPtage) + } else { + removeProgress() + showMessage(getString(R.string.enter_valid_lift_value_error)) + } + } else { + removeProgress() + showMessage(getString(R.string.enter_lift_value_error)) + } + + } + } + binding.btnWindowTilt.setOnClickListener { + showMatterProgressDialog(getString(R.string.matter_door_lock_in_progress)) + scope.launch { + if (!binding.etTilt.text.isNullOrEmpty()) { + var tiltPtage = binding.etTilt.text.toString().toIntOrNull() + if (tiltPtage != null && tiltPtage in 1..100) { + tiltPtage = 100 - tiltPtage + sendWindowTiltCommandClick(tiltPtage) + } else { + removeProgress() + showMessage(getString(R.string.enter_valid_tilt_value_error)) + } + } else { + removeProgress() + showMessage(getString(R.string.enter_tilt_value_error)) + } + + } + } (activity as MatterDemoActivity).hideQRScanner() //back handling - requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressedCallback) + requireActivity().onBackPressedDispatcher.addCallback( + viewLifecycleOwner, + onBackPressedCallback + ) + } + + private suspend fun readTargetPositionLift() { + getWindowClusterForDevice().readTargetPositionLiftPercent100thsAttribute(object : + ChipClusters.WindowCoveringCluster.TargetPositionLiftPercent100thsAttributeCallback { + override fun onSuccess(value: Int?) { + removeProgress() + // binding.etLift.setText(value?.div(100).toString()) + when (value?.div(100)) { + 0 -> { + binding.btnMatterWindowState.setImageResource(R.drawable.w0) + scope.launch { + sendWindowOpenCommandClick() + } + } + + in 1..10 -> binding.btnMatterWindowState.setImageResource(R.drawable.w1) + in 11..20 -> binding.btnMatterWindowState.setImageResource(R.drawable.w2) + in 21..30 -> binding.btnMatterWindowState.setImageResource(R.drawable.w3) + in 31..40 -> binding.btnMatterWindowState.setImageResource(R.drawable.w4) + in 41..50 -> binding.btnMatterWindowState.setImageResource(R.drawable.w5) + in 51..60 -> binding.btnMatterWindowState.setImageResource(R.drawable.w6) + in 61..70 -> binding.btnMatterWindowState.setImageResource(R.drawable.w7) + in 71..80 -> binding.btnMatterWindowState.setImageResource(R.drawable.w8) + in 81..90 -> binding.btnMatterWindowState.setImageResource(R.drawable.w9) + in 91..100 -> binding.btnMatterWindowState.setImageResource(R.drawable.w10) + else -> null + } + } + + override fun onError(ex: java.lang.Exception?) { + removeProgress() + Timber.tag(TAG).e("Read LiftPercentage command failure: $ex") + } + }) + } + + private suspend fun readTargetPositionTilt() { + getWindowClusterForDevice().readTargetPositionTiltPercent100thsAttribute(object : + ChipClusters.WindowCoveringCluster.TargetPositionTiltPercent100thsAttributeCallback { + override fun onSuccess(value: Int?) { + removeProgress() + // binding.etTilt.setText(value?.div(100).toString()) + when (value?.div(100)) { + in 0..10 -> binding.btnMatterWindowState.alpha = 0.1f + + in 11..20 -> binding.btnMatterWindowState.alpha = 0.15f + + in 21..30 -> binding.btnMatterWindowState.alpha = 0.25f + + in 31..40 -> binding.btnMatterWindowState.alpha = 0.35f + + in 41..50 -> binding.btnMatterWindowState.alpha = 0.45f + + in 51..60 -> binding.btnMatterWindowState.alpha = 0.55f + + in 61..70 -> binding.btnMatterWindowState.alpha = 0.65f + + in 71..80 -> binding.btnMatterWindowState.alpha = 0.75f + + in 81..90 -> binding.btnMatterWindowState.alpha = 0.85f + + in 91..100 -> binding.btnMatterWindowState.alpha = 0.95f + } + } + + override fun onError(ex: java.lang.Exception?) { + removeProgress() + Timber.tag(TAG).e("Read LiftPercentage command failure: $ex") + } + + }) } private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - if (requireActivity().supportFragmentManager.getBackStackEntryCount() > 0) { - requireActivity().supportFragmentManager.popBackStack(); + if (requireActivity().supportFragmentManager.backStackEntryCount > 0) { + requireActivity().supportFragmentManager.popBackStack() } else { FragmentUtils.getHost(this@MatterWindowCoverFragment, CallBackHandler::class.java) .onBackHandler() @@ -232,17 +356,19 @@ class MatterWindowCoverFragment : Fragment() { getWindowClusterForDevice().downOrClose( object : ChipClusters.DefaultClusterCallback { override fun onSuccess() { - Timber.tag(TAG).d( "downOrClose command Success") + Timber.tag(TAG).d("downOrClose command Success") SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) - binding.btnMatterWindowState.setImageResource(R.drawable.window_close) + binding.btnMatterWindowState.alpha = 1.0f + binding.btnMatterWindowState.setImageResource(R.drawable.w10) + removeProgress() } override fun onError(error: Exception?) { SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) showMessageDialog(getString(R.string.matter_device_offline_text)) - - Log.e(TAG, "downOrClose command failure", error) + Timber.tag(TAG).e("downOrClose command failure: $error") + removeProgress() } } @@ -254,23 +380,73 @@ class MatterWindowCoverFragment : Fragment() { object : ChipClusters.DefaultClusterCallback { override fun onSuccess() { SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) - Timber.tag(TAG).d( "upOrOpen command Success") - binding.btnMatterWindowState.setImageResource(R.drawable.window_open) + Timber.tag(TAG).d("upOrOpen command Success") + removeProgress() + binding.btnMatterWindowState.setImageResource(R.drawable.w0) } override fun onError(error: Exception?) { SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) showMessageDialog(getString(R.string.matter_device_offline_text)) - // showMessage("Matter device is offline") - // showMessage("upOrOpen command failure $error") - Log.e(TAG, "upOrOpen command failure", error) + removeProgress() + Timber.tag(TAG).e("upOrOpen command failure: $error") } - } + }, TIME_OUT + ) + + } + + private suspend fun sendWindowLiftCommandClick(liftPtage: Int) { + + getWindowClusterForDevice().goToLiftPercentage( + object : ChipClusters.DefaultClusterCallback { + override fun onSuccess() { + SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) + Timber.tag(TAG).d("LiftPercentage command Success") + scope.launch { + readTargetPositionLift() + removeProgress() + } + + } + + + override fun onError(error: Exception?) { + SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) + showMessageDialog(getString(R.string.matter_device_offline_text)) + removeProgress() + Timber.tag(TAG).e("LiftPercentage command failure: $error") + } + + }, liftPtage * 100 ) } + private suspend fun sendWindowTiltCommandClick(tiltPtage: Int) { + getWindowClusterForDevice().goToTiltPercentage( + object : ChipClusters.DefaultClusterCallback { + override fun onSuccess() { + SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, true) + Timber.tag(TAG).d("TiltPercentage command Success") + scope.launch { + readTargetPositionTilt() + } + } + + + override fun onError(error: Exception?) { + SharedPrefsUtils.updateDeviceByDeviceId(mPrefs, deviceId, false) + showMessageDialog(getString(R.string.matter_device_offline_text)) + + Timber.tag(TAG).e("LiftPercentage command failure: $error") + } + + }, tiltPtage * 100 + ) + } + private suspend fun getWindowClusterForDevice(): ChipClusters.WindowCoveringCluster { return ChipClusters.WindowCoveringCluster( ChipClient.getConnectedDevicePointer(requireContext(), deviceId), @@ -288,26 +464,27 @@ class MatterWindowCoverFragment : Fragment() { inner class WindowCoverChipControllerCallback : GenericChipDeviceListener() { override fun onConnectDeviceComplete() {} - override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { - Timber.tag(TAG).d( "onCommissioningComplete for nodeId $nodeId: $errorCode") + override fun onCommissioningComplete(nodeId: Long, errorCode: Long) { + Timber.tag(TAG).d("onCommissioningComplete for nodeId $nodeId: $errorCode") //showMessage("Address update complete for nodeId $nodeId with code $errorCode") } override fun onNotifyChipConnectionClosed() { - Timber.tag(TAG).d( "onNotifyChipConnectionClosed") + Timber.tag(TAG).d("onNotifyChipConnectionClosed") } override fun onCloseBleComplete() { - Timber.tag(TAG).d( "onCloseBleComplete") + Timber.tag(TAG).d("onCloseBleComplete") } - override fun onError(error: Throwable) { + override fun onError(error: Throwable?) { super.onError(error) - Timber.tag(TAG).d( "onError: $error") + Timber.tag(TAG).d("onError: $error") } } + interface CallBackHandler { fun onBackHandler() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/manager/BluetoothManager.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/manager/BluetoothManager.kt index b93b6904..519555be 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/manager/BluetoothManager.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/manager/BluetoothManager.kt @@ -196,7 +196,7 @@ class BluetoothManager : BleCallback { wrappedCallback.onCharacteristicChanged(gatt, characteristic) } - @SuppressLint("MissingPermission") + @SuppressLint("MissingPermission", "LogNotTimber") override fun onCharacteristicRead( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/model/CHIPDeviceInfo.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/model/CHIPDeviceInfo.kt index 89b95536..17a0aabd 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/model/CHIPDeviceInfo.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/model/CHIPDeviceInfo.kt @@ -1,10 +1,12 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.model -import android.os.Parcel import android.os.Parcelable -import chip.setuppayload.DiscoveryCapability -import chip.setuppayload.SetupPayload +import android.util.Log import kotlinx.android.parcel.Parcelize +import matter.onboardingpayload.OnboardingPayload +import matter.onboardingpayload.OnboardingPayloadException +import matter.onboardingpayload.OptionalQRCodeInfoType +import timber.log.Timber @Parcelize data class CHIPDeviceInfo( @@ -15,37 +17,67 @@ data class CHIPDeviceInfo( val setupPinCode: Long = 0L, var commissioningFlow: Int = 0, val optionalQrCodeInfoMap: Map = mapOf(), - val discoveryCapabilities: Set = setOf(), + val discoveryCapabilities: MutableSet = mutableSetOf(), val isShortDiscriminator: Boolean = false, + val serialNumber: String = "", val ipAddress: String? = null, var networkType: String? = null, + val port: Int = 5540 +) : Parcelable { + + fun toSetupPayload(): OnboardingPayload { + val onboardingPayload = + OnboardingPayload( + version, + vendorId, + productId, + commissioningFlow, + discoveryCapabilities, + discriminator, + isShortDiscriminator, + setupPinCode + ) + if (serialNumber.isNotEmpty()) { + onboardingPayload.addSerialNumber(serialNumber) + } + optionalQrCodeInfoMap.forEach { (_, info) -> + if (info.type == OptionalQRCodeInfoType.TYPE_STRING && info.data != null) { + onboardingPayload.addOptionalVendorData(info.tag, info.data) + } else { + onboardingPayload.addOptionalVendorData(info.tag, info.intDataValue) + } + } + return onboardingPayload + } - ) : Parcelable { companion object { + private const val TAG = "CHIPDeviceInfo" + fun fromSetupPayload( - setupPayload: SetupPayload, + setupPayload: OnboardingPayload, isShortDiscriminator: Boolean = false ): CHIPDeviceInfo { + val serialNumber = + try { + setupPayload.getSerialNumber() + } catch (e: OnboardingPayloadException) { + Timber.tag(TAG).e(e, "serialNumber Exception: ${e.message}") + "" + } return CHIPDeviceInfo( setupPayload.version, setupPayload.vendorId, setupPayload.productId, - setupPayload.discriminator, + setupPayload.getLongDiscriminatorValue(), setupPayload.setupPinCode, setupPayload.commissioningFlow, - setupPayload.optionalQRCodeInfo.mapValues { (_, info) -> - QrCodeInfo( - info.tag, - info.type, - info.data, - info.int32 - ) + setupPayload.getAllOptionalVendorData().associate { info -> + info.tag to QrCodeInfo(info.tag, info.type, info.data, info.int32) }, setupPayload.discoveryCapabilities, - isShortDiscriminator + setupPayload.hasShortDiscriminator, + serialNumber ) } } - - -} +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/model/DishWasherModel.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/model/DishWasherModel.kt new file mode 100644 index 00000000..21ce0710 --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/model/DishWasherModel.kt @@ -0,0 +1,6 @@ +package com.siliconlabs.bledemo.features.demo.matter_demo.model + +object DishWasherModel { + var dishwasherCurrentRunningState = "" + var remainingTimeLeft : Long = 0 +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/model/QrCodeInfo.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/model/QrCodeInfo.kt index 1feeb081..5b9da5ec 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/model/QrCodeInfo.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/model/QrCodeInfo.kt @@ -1,13 +1,13 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.model import android.os.Parcelable -import chip.setuppayload.OptionalQRCodeInfo -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize +import matter.onboardingpayload.OptionalQRCodeInfoType @Parcelize data class QrCodeInfo( val tag: Int, - val type: OptionalQRCodeInfo.OptionalQRCodeInfoType, - val data: String, + val type: OptionalQRCodeInfoType, + val data: String?, val intDataValue: Int -):Parcelable +) : Parcelable diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/ChipClient.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/ChipClient.kt index 5ae8ab66..14b8fe66 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/ChipClient.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/ChipClient.kt @@ -1,11 +1,7 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.utils import android.content.Context -import android.os.Handler -import android.os.Looper -import android.util.Log import android.widget.Toast -import androidx.camera.core.impl.utils.ContextUtil.getApplicationContext import chip.devicecontroller.ChipDeviceController import chip.devicecontroller.ControllerParams import chip.devicecontroller.GetConnectedDeviceCallbackJni @@ -18,9 +14,10 @@ import chip.platform.NsdManagerServiceResolver import chip.platform.PreferencesConfigurationManager import chip.platform.PreferencesKeyValueStoreManager import com.siliconlabs.bledemo.features.demo.matter_demo.attestation.ExampleAttestationTrustStoreDelegate +import kotlinx.coroutines.suspendCancellableCoroutine +import timber.log.Timber import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException -import kotlin.coroutines.suspendCoroutine object ChipClient { @@ -31,10 +28,12 @@ object ChipClient { fun getDeviceController(context: Context): ChipDeviceController { getAndroidChipPlatform(context) + if (!this::chipDeviceController.isInitialized) { chipDeviceController = ChipDeviceController( ControllerParams.newBuilder() .setControllerVendorId(VENDOR_ID) + .setEnableServerInteractions(true) .build() ) chipDeviceController.setAttestationTrustStoreDelegate( @@ -51,7 +50,10 @@ object ChipClient { AndroidBleManager(), PreferencesKeyValueStoreManager(context), PreferencesConfigurationManager(context), - NsdManagerServiceResolver(context), + NsdManagerServiceResolver( + context, + NsdManagerServiceResolver.NsdManagerResolverAvailState() + ), NsdManagerServiceBrowser(context), ChipMdnsCallbackImpl(), DiagnosticDataProviderImpl(context) ) @@ -63,27 +65,60 @@ object ChipClient { Toast.makeText(context, msg, Toast.LENGTH_SHORT).show() } + /* suspend fun getConnectedDevicePointer(context: Context, nodeId: Long): Long { + // TODO (#21539) This is a memory leak because we currently never call releaseConnectedDevicePointer + // once we are done with the returned device pointer. Memory leak was introduced since the refactor + // that introduced it was very large in order to fix a use after free, which was considered to be + // worse than the memory leak that was introduced. + + return suspendCancellableCoroutine { continuation -> + getDeviceController(context).getConnectedDevicePointer( + nodeId, + object : GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback { + override fun onDeviceConnected(devicePointer: Long) { + Timber.tag(TAG).d("Got connected device pointer") + continuation.resume(devicePointer) + } + + override fun onConnectionFailure(nodeId: Long, error: Exception) { + val errorMessage = "Unable to get connected device with nodeId $nodeId" + Timber.tag(TAG).e(error, errorMessage) + continuation.resumeWithException(IllegalStateException(errorMessage)) + } + } + ) + + } + }*/ + suspend fun getConnectedDevicePointer(context: Context, nodeId: Long): Long { - // TODO (#21539) This is a memory leak because we currently never call releaseConnectedDevicePointer - // once we are done with the returned device pointer. Memory leak was introduced since the refactor - // that introduced it was very large in order to fix a use after free, which was considered to be - // worse than the memory leak that was introduced. - return suspendCoroutine { continuation -> - getDeviceController(context).getConnectedDevicePointer( - nodeId, - object : GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback { - override fun onDeviceConnected(devicePointer: Long) { - Log.d(TAG, "Got connected device pointer") - continuation.resume(devicePointer) - } + return suspendCancellableCoroutine { continuation -> + try { + getDeviceController(context).getConnectedDevicePointer( + nodeId, + object : GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback { + override fun onDeviceConnected(devicePointer: Long) { + Timber.tag(TAG).d("Got connected device pointer") + continuation.resume(devicePointer) + } - override fun onConnectionFailure(nodeId: Long, error: Exception) { - // showMessage("Device is offline",context) - val errorMessage = "Unable to get connected device with nodeId $nodeId" - Log.e(TAG, errorMessage, error) - continuation.resumeWithException(IllegalStateException(errorMessage)) + override fun onConnectionFailure(nodeId: Long, error: Exception) { + val errorMessage = "Unable to get connected device with nodeId $nodeId" + Timber.tag(TAG).e(error, errorMessage) + if (continuation.isActive) { + continuation.resumeWithException(IllegalStateException(errorMessage)) + } + // continuation.resumeWithException(IllegalStateException(errorMessage)) + } } - }) + ) + } catch (e: Exception) { + val errorMessage = "Failed to initiate connection for nodeId $nodeId" + Timber.tag(TAG).e(e, errorMessage) + if (continuation.isActive) { + continuation.resumeWithException(e) + } + } } } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/CustomInputDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/CustomInputDialog.kt index 56e8f25f..a26ca1ac 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/CustomInputDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/CustomInputDialog.kt @@ -66,10 +66,6 @@ class CustomInputDialog : DialogFragment() { onOkButtonClick() } - if (dialog != null && dialog!!.window != null) { - dialog!!.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - dialog!!.window!!.requestFeature(Window.FEATURE_NO_TITLE); - } return builder.create() } @@ -77,6 +73,12 @@ class CustomInputDialog : DialogFragment() { super.onStart() val dialog: Dialog? = dialog if (dialog != null) { +// if (dialog != null && dialog!!.window != null) { +// dialog!!.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) +// dialog!!.window!!.requestFeature(Window.FEATURE_NO_TITLE); +// +// } + dialog.setCanceledOnTouchOutside(false) dialog.window!! .setLayout( (getScreenWidth(requireActivity()) * MatterOTBRInputDialogFragment.WINDOW_SIZE).toInt(), @@ -87,6 +89,7 @@ class CustomInputDialog : DialogFragment() { private fun getScreenWidth(activity: Activity): Int { val size = Point() + activity.windowManager.defaultDisplay.getSize(size) return size.x } @@ -99,11 +102,15 @@ class CustomInputDialog : DialogFragment() { dismiss() } else { if (enteredText.isEmpty()) { - Toast.makeText(requireContext(), - getString(R.string.please_enter_device_name), Toast.LENGTH_SHORT).show() + Toast.makeText( + requireContext(), + getString(R.string.please_enter_device_name), Toast.LENGTH_SHORT + ).show() } else { - Toast.makeText(requireContext(), - getString(R.string.please_enter_valid_device_name), Toast.LENGTH_SHORT).show() + Toast.makeText( + requireContext(), + getString(R.string.please_enter_valid_device_name), Toast.LENGTH_SHORT + ).show() } } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/CustomProgressDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/CustomProgressDialog.kt index d8539634..baf8d60d 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/CustomProgressDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/CustomProgressDialog.kt @@ -4,23 +4,43 @@ import android.app.Dialog import android.content.Context import android.os.Bundle import android.view.LayoutInflater +import android.view.View +import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.databinding.CustomProgressDialogBinding class CustomProgressDialog(context: Context) : Dialog(context) { private lateinit var binding: CustomProgressDialogBinding + private var cancelButtonVisible: Boolean = false + private var cancelButtonClickListener: (() -> Unit)? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setCancelable(false) setCanceledOnTouchOutside(false) + + if (cancelButtonVisible) { + binding.customProgressBar.visibility = View.GONE + binding.customMessage.visibility = View.GONE + binding.layoutMatterCommissioning.visibility = View.VISIBLE + binding.btnCancel.setOnClickListener { + cancelButtonClickListener?.invoke() + } + } else { + binding.customProgressBar.visibility = View.VISIBLE + binding.customMessage.visibility = View.VISIBLE + binding.layoutMatterCommissioning.visibility = View.GONE + } } fun setMessage(message: String) { binding = CustomProgressDialogBinding.inflate(LayoutInflater.from(context)) - setContentView(binding.root) - // Now you can access views through binding binding.customMessage.text = message } + + fun setCustomButtonVisible(visible: Boolean, clickListener: (() -> Unit)?) { + cancelButtonVisible = visible + cancelButtonClickListener = clickListener + } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/DeviceIDUtil.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/DeviceIDUtil.kt index 5341199e..f3c50937 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/DeviceIDUtil.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/DeviceIDUtil.kt @@ -3,6 +3,9 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.utils import android.content.Context import android.content.SharedPreferences import com.siliconlabs.bledemo.BuildConfig +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale object DeviceIDUtil { @@ -19,12 +22,16 @@ object DeviceIDUtil { } else { deviceID = DEFAULT_DEVICE_ID_PROD_ENV } + val timestamp = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(Date()) val prefs = getPrefs(context) + val updatedDeviceID = "$deviceID$timestamp" + println("DeviceID :$updatedDeviceID") + println("DeviceID Long :${updatedDeviceID.toLong()}") return if (prefs.contains(DEVICE_ID_PREFS_KEY)) { - prefs.getLong(DEVICE_ID_PREFS_KEY, deviceID) + prefs.getLong(DEVICE_ID_PREFS_KEY, updatedDeviceID.toLong()) } else { - prefs.edit().putLong(DEVICE_ID_PREFS_KEY, deviceID).apply() - deviceID + prefs.edit().putLong(DEVICE_ID_PREFS_KEY, updatedDeviceID.toLong()).apply() + updatedDeviceID.toLong() } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/MatterDialogDeviceInfoFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/MatterDialogDeviceInfoFragment.kt index 522a05e5..db25651f 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/MatterDialogDeviceInfoFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/MatterDialogDeviceInfoFragment.kt @@ -8,7 +8,7 @@ import android.view.ViewGroup import androidx.fragment.app.DialogFragment import com.siliconlabs.bledemo.databinding.DialogDeviceInfoLayoutBinding import com.siliconlabs.bledemo.features.iop_test.utils.DialogDeviceInfoFragment -import kotlinx.android.synthetic.main.adapter_scanned_device.title + class MatterDialogDeviceInfoFragment: DialogFragment() { private var title: String? = null diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/SharedPrefsUtils.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/SharedPrefsUtils.kt index 1162fe56..25cd8c15 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/SharedPrefsUtils.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/utils/SharedPrefsUtils.kt @@ -1,7 +1,7 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.utils + import android.content.SharedPreferences import com.google.gson.Gson -import android.content.Context import com.google.gson.reflect.TypeToken import com.siliconlabs.bledemo.features.demo.matter_demo.model.MatterScannedResultModel @@ -15,7 +15,11 @@ object SharedPrefsUtils { return myList ?: ArrayList() } - fun saveDevicesToPref(mPrefs: SharedPreferences, scannedDeviceList: ArrayList) { + + fun saveDevicesToPref( + mPrefs: SharedPreferences, + scannedDeviceList: ArrayList + ) { val prefsEditor: SharedPreferences.Editor = mPrefs.edit() val gson = Gson() val json = gson.toJson(scannedDeviceList) @@ -23,13 +27,18 @@ object SharedPrefsUtils { prefsEditor.apply() } - fun updateDeviceAtIndex(mPrefs: SharedPreferences, index: Int, updatedDevice: MatterScannedResultModel) { + fun updateDeviceAtIndex( + mPrefs: SharedPreferences, + index: Int, + updatedDevice: MatterScannedResultModel + ) { val existingList = retrieveSavedDevices(mPrefs) if (index >= 0 && index < existingList.size) { existingList[index] = updatedDevice saveDevicesToPref(mPrefs, existingList) } } + fun updateDeviceByDeviceId(mPrefs: SharedPreferences, deviceId: Long, newValue: Boolean) { val deviceList = retrieveSavedDevices(mPrefs) var updated = false @@ -46,6 +55,157 @@ object SharedPrefsUtils { saveDevicesToPref(mPrefs, deviceList) } } - const val ARG_ADDED_DEVICE_INFO_LIST = "addedDeviceInfoList" + + //Total energy + fun saveDishwasherTotalEnergyConsumption( + mPrefs: SharedPreferences, + totalEnergyConsumed: Float + ) { + val prefsEditor: SharedPreferences.Editor = mPrefs.edit() + prefsEditor.apply { + putFloat(TOTAL_ENERGY_DISHWASHER, totalEnergyConsumed) + apply() + } + + } + + fun getDishwasherTotalEnergyConsumption(mPrefs: SharedPreferences): Float { + + return mPrefs.getFloat(TOTAL_ENERGY_DISHWASHER, 0f) + } + + //average energy + fun saveDishwasherAverageEnergyPerCycle( + mPrefs: SharedPreferences, + averageEnergyPerCycleConsumed: Float + ) { + val prefsEditor: SharedPreferences.Editor = mPrefs.edit() + prefsEditor.apply { + putFloat(AVERAGE_ENERGY_PER_CYCLE_DISHWASHER, averageEnergyPerCycleConsumed) + apply() + } + + } + + fun getDishwasherAverageEnergyPerCycle(mPrefs: SharedPreferences): Float{ + + return mPrefs.getFloat(AVERAGE_ENERGY_PER_CYCLE_DISHWASHER, 0f) + } + + //inCurrent cycle + fun saveDishwasherInCurrentCycleEnergyConsumed( + mPrefs: SharedPreferences, + inCurrentCycleEnergyConsumed: Float + ) { + val prefsEditor: SharedPreferences.Editor = mPrefs.edit() + prefsEditor.apply { + putFloat(DISHWASHER_IN_CURRENT_CYCLE_CONSUMED, inCurrentCycleEnergyConsumed) + apply() + } + } + + fun getDishwasherInCurrentCycleEnergyConsumed(mPrefs: SharedPreferences): Float { + + return mPrefs.getFloat(DISHWASHER_IN_CURRENT_CYCLE_CONSUMED, 0f) + } + + //completedCycleCount + fun saveDishwasherCompletedCycleCount( + mPrefs: SharedPreferences, + completedCycleCount: Int + ) { + val prefsEditor: SharedPreferences.Editor = mPrefs.edit() + prefsEditor.apply { + putInt(DISHWASHER_COMPLETED_CYCLE_COUNT, completedCycleCount) + apply() + } + } + + fun getDishwasherCompletedCycleCount(mPrefs: SharedPreferences): Int { + + return mPrefs.getInt(DISHWASHER_COMPLETED_CYCLE_COUNT, 0) + } + + + fun saveDishwasherTimeLeftFormatted(mPrefs: SharedPreferences, timeLeftInMillSeconds: Long) { + val prefsEditor: SharedPreferences.Editor = mPrefs.edit() + prefsEditor.apply { + putLong(DISHWASHER_TOTAL_TIME_LEFT, timeLeftInMillSeconds) + apply() + } + } + + + fun getDishwasherTimeLeftFormatted(mPrefs: SharedPreferences): Long { + return mPrefs.getLong(DISHWASHER_TOTAL_TIME_LEFT, 600000) + } + + fun saveDishwasherAppliedCycleStates(mPrefs: SharedPreferences, cycleStates: String) { + val prefsEditor: SharedPreferences.Editor = mPrefs.edit() + prefsEditor.apply { + putString( + DISHWASHER_CYCLE_STATES, + cycleStates + ) + apply() + } + } + + fun getDishwasherAppliedCycleStates(mPrefs: SharedPreferences): String? { + + return mPrefs.getString(DISHWASHER_CYCLE_STATES, null) + } + + + //SAVE progressBar + fun saveDishwasherCompletedCycleProgressBar( + mPrefs: SharedPreferences, + completedProgressBarCount: Int + ) { + val prefsEditor: SharedPreferences.Editor = mPrefs.edit() + prefsEditor.apply { + putInt(DISHWASHER_COMPLETED_PROGRESS_BAR_COUNT, completedProgressBarCount) + apply() + } + } + + fun getDishwasherCompletedCycleProgressBar(mPrefs: SharedPreferences): Int { + return mPrefs.getInt(DISHWASHER_COMPLETED_PROGRESS_BAR_COUNT, 0) + } + + fun clearDishwasherSharedPreferences(preferences: SharedPreferences) { + preferences.edit().apply { + clear() // Clears all data in the SharedPreferences + apply() // Commit the changes asynchronously + } + } + + fun saveTheDishwasherStateIfTheUserIsInPauseState(mPrefs: SharedPreferences, savePauseOrStopState: Boolean): Boolean { + val prefsEditor: SharedPreferences.Editor = mPrefs.edit() + + // Save the state and apply the changes + prefsEditor.putBoolean(DISHWASHER_ON_CYCLE_PAUSE_RESUME_STATE, savePauseOrStopState) + prefsEditor.apply() + + // Return true if the operation was successful (SharedPreferences doesn't give an explicit success/failure status) + return true + } + + fun getDishwasherStateIfUserIsInPauseState(mPrefs: SharedPreferences): Boolean { + // Retrieve the state using the same key, with a default value of false if not found + return mPrefs.getBoolean(DISHWASHER_ON_CYCLE_PAUSE_RESUME_STATE, false) + } + + + const val ARG_ADDED_DEVICE_INFO_LIST = "addedDeviceInfoList" + const val TOTAL_ENERGY_DISHWASHER = "totalEnergyDishwasher" + const val AVERAGE_ENERGY_PER_CYCLE_DISHWASHER = "averageEnergyPerCycle" + const val DISHWASHER_IN_CURRENT_CYCLE_CONSUMED = "inCurrentCycle" + const val DISHWASHER_COMPLETED_CYCLE_COUNT = "cycleCount" + const val DISHWASHER_TOTAL_TIME_LEFT = "timeLeft" + const val DISHWASHER_CYCLE_STATES = "dishwasherCycleStates" + const val DISHWASHER_COMPLETED_PROGRESS_BAR_COUNT = "dishwasherCompletedProgressBarCount" + const val DISHWASHER_ON_CYCLE_PAUSE_RESUME_STATE = "dishwasherOnPauseOrStopState" + } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/viewholder/MatterItemViewModel.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/viewholder/MatterItemViewModel.kt index baa8dcc7..e73a1ffe 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/viewholder/MatterItemViewModel.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/viewholder/MatterItemViewModel.kt @@ -5,16 +5,17 @@ import androidx.recyclerview.widget.RecyclerView import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.databinding.MatterScannedListItemBinding import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.CONTACT_SENSOR_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.DISHWASHER_TYPE import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.ENHANCED_COLOR_LIGHT_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.LIGHTNING_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.LOCK_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.DIMMABLE_LIGHT_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.DOOR_LOCK_TYPE import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.OCCUPANCY_SENSOR_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.ONOFF_LIGHT_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.PLUG_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.TEMPERATURE_COLOR_LIGHT_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.ON_OFF_LIGHT_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.DIMMABLE_PLUG_IN_UNIT_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.COLOR_TEMPERATURE_LIGHT_TYPE import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.TEMPERATURE_SENSOR_TYPE import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.THERMOSTAT_TYPE -import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.WINDOW_TYPE +import com.siliconlabs.bledemo.features.demo.matter_demo.fragments.MatterScannedResultFragment.Companion.WINDOW_COVERING_TYPE import com.siliconlabs.bledemo.features.demo.matter_demo.model.MatterScannedResultModel class MatterItemViewModel( @@ -27,14 +28,15 @@ class MatterItemViewModel( binding.textViewHeader.text = get.matterName when (get.deviceType) { - LIGHTNING_TYPE, ENHANCED_COLOR_LIGHT_TYPE, ONOFF_LIGHT_TYPE, TEMPERATURE_COLOR_LIGHT_TYPE -> binding.imageview.setImageResource(R.drawable.matter_light_list) - THERMOSTAT_TYPE -> binding.imageview.setImageResource(R.drawable.matter_thermostat) - WINDOW_TYPE -> binding.imageview.setImageResource(R.drawable.matter_window_close) - LOCK_TYPE -> binding.imageview.setImageResource(R.drawable.matter_door_lock) - OCCUPANCY_SENSOR_TYPE -> binding.imageview.setImageResource(R.drawable.matter_occupancy_sensor_list) - CONTACT_SENSOR_TYPE -> binding.imageview.setImageResource(R.drawable.matter_contact_sensor_list) - TEMPERATURE_SENSOR_TYPE -> binding.imageview.setImageResource(R.drawable.matter_thermometer_list) - PLUG_TYPE -> binding.imageview.setImageResource(R.drawable.matter_plug_off) + DIMMABLE_LIGHT_TYPE, ENHANCED_COLOR_LIGHT_TYPE, ON_OFF_LIGHT_TYPE, COLOR_TEMPERATURE_LIGHT_TYPE -> binding.imageView.setImageResource(R.drawable.matter_light_list) + THERMOSTAT_TYPE -> binding.imageView.setImageResource(R.drawable.matter_thermostat) + WINDOW_COVERING_TYPE -> binding.imageView.setImageResource(R.drawable.matter_window_close) + DOOR_LOCK_TYPE -> binding.imageView.setImageResource(R.drawable.matter_door_lock) + OCCUPANCY_SENSOR_TYPE -> binding.imageView.setImageResource(R.drawable.matter_occupancy_sensor_list) + CONTACT_SENSOR_TYPE -> binding.imageView.setImageResource(R.drawable.matter_contact_sensor_list) + TEMPERATURE_SENSOR_TYPE -> binding.imageView.setImageResource(R.drawable.matter_thermometer_list) + DIMMABLE_PLUG_IN_UNIT_TYPE -> binding.imageView.setImageResource(R.drawable.matter_plug_off) + DISHWASHER_TYPE -> binding.imageView.setImageResource(R.drawable.matter_dishwasher_list) else -> println("To Be Implemented...") } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/activities/RangeTestActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/activities/RangeTestActivity.kt index 91eb1c00..8a431ee8 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/activities/RangeTestActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/activities/RangeTestActivity.kt @@ -7,6 +7,7 @@ import android.os.Bundle import android.os.CountDownTimer import android.os.Handler import android.os.Looper +import android.view.LayoutInflater import android.view.MenuItem import android.view.WindowManager import android.widget.TextView @@ -16,6 +17,7 @@ import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.base.activities.BaseDemoActivity import com.siliconlabs.bledemo.bluetooth.ble.* import com.siliconlabs.bledemo.bluetooth.services.BluetoothService +import com.siliconlabs.bledemo.databinding.ActivityRangeTestBinding import com.siliconlabs.bledemo.features.demo.range_test.dialogs.RangeTestModeDialog import com.siliconlabs.bledemo.features.demo.range_test.fragments.RangeTestFragment import com.siliconlabs.bledemo.features.demo.range_test.models.* @@ -25,7 +27,6 @@ import com.siliconlabs.bledemo.features.demo.range_test.presenters.RangeTestPres import com.siliconlabs.bledemo.home_screen.dialogs.SelectDeviceDialog import com.siliconlabs.bledemo.utils.BLEUtils.setNotificationForCharacteristic import com.siliconlabs.bledemo.utils.Notifications -import kotlinx.android.synthetic.main.activity_range_test.* import java.nio.ByteBuffer import java.nio.ByteOrder import java.util.* @@ -39,7 +40,7 @@ import kotlin.math.abs @SuppressLint("MissingPermission") class RangeTestActivity : BaseDemoActivity(), Controller { - private var lowerLimit: Int = 0 + private var lowerLimit: Int = 0 private var upperLimit: Int = 0 private var descriptor2906: BluetoothGattDescriptor? = null private var activeDeviceId = 1 @@ -53,7 +54,7 @@ class RangeTestActivity : BaseDemoActivity(), Controller { private var timerStarted = false private var retryAttempts = 0 - + private lateinit var binding: ActivityRangeTestBinding private var txUpdateTimer: TxUpdateTimer? = null @@ -125,23 +126,26 @@ class RangeTestActivity : BaseDemoActivity(), Controller { public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_range_test) + binding = ActivityRangeTestBinding.inflate(LayoutInflater.from(baseContext)) + setContentView(binding.root) + //setContentView(R.layout.activity_range_test) setupUiListeners() } private fun setupUiListeners() { - tv_device1_tab.setOnClickListener { - changeDevice(tv_device1_tab, 1) + + binding.tvDevice1Tab.setOnClickListener { + changeDevice(binding.tvDevice1Tab, 1) } - tv_device2_tab.setOnClickListener { - changeDevice(tv_device2_tab, 2) + binding.tvDevice2Tab.setOnClickListener { + changeDevice(binding.tvDevice2Tab, 2) } } override fun onBluetoothServiceBound() { - selectTab(tv_device1_tab) - changeDevice(tv_device1_tab, 1) + selectTab(binding.tvDevice1Tab) + changeDevice(binding.tvDevice1Tab, 1) } @@ -191,8 +195,8 @@ class RangeTestActivity : BaseDemoActivity(), Controller { } }) }.also { - it.show(supportFragmentManager, "select_device_dialog") - } + it.show(supportFragmentManager, "select_device_dialog") + } } else { showRangeTestFragment(presenter.getMode()) } @@ -567,11 +571,11 @@ class RangeTestActivity : BaseDemoActivity(), Controller { presenter.resetDeviceAt(resetDeviceId) if (resetDeviceId == 1) { - tv_device1_tab.text = getString(R.string.range_test_no_device) - changeDevice(tv_device1_tab, 1) + binding.tvDevice1Tab.text = getString(R.string.range_test_no_device) + changeDevice(binding.tvDevice1Tab, 1) } else { - tv_device2_tab.text = getString(R.string.range_test_no_device) - changeDevice(tv_device2_tab, 2) + binding.tvDevice2Tab.text = getString(R.string.range_test_no_device) + changeDevice(binding.tvDevice2Tab, 2) } } } @@ -785,9 +789,9 @@ class RangeTestActivity : BaseDemoActivity(), Controller { ) { super.onCharacteristicRead(gatt, characteristic, status) - val descriptorUuid=UUID.fromString("00002906-0000-1000-8000-00805f9b34fb") - if(characteristic!=null){ - descriptor2906=characteristic.getDescriptor(descriptorUuid) + val descriptorUuid = UUID.fromString("00002906-0000-1000-8000-00805f9b34fb") + if (characteristic != null) { + descriptor2906 = characteristic.getDescriptor(descriptorUuid) } if (status != BluetoothGatt.GATT_SUCCESS) { handleConnectionError() @@ -806,9 +810,8 @@ class RangeTestActivity : BaseDemoActivity(), Controller { if (descriptors.size > 1) { queueReadDescriptor(gatt, characteristic, descriptors[descriptors.size - 1]) } - } - else{ - if (gattCharacteristic===GattCharacteristic.RangeTestChannel){ + } else { + if (gattCharacteristic === GattCharacteristic.RangeTestChannel) { val descriptors = characteristic.descriptors if (descriptors.size > 1) { descriptor2906?.let { queueReadDescriptor(gatt, characteristic, it) } @@ -828,16 +831,18 @@ class RangeTestActivity : BaseDemoActivity(), Controller { if (descriptor?.uuid == UUID.fromString("00002906-0000-1000-8000-00805f9b34fb")) { try { val validRangeValue = descriptor.value - lowerLimit = (validRangeValue[1].toInt() and 0xFF) shl 8 or (validRangeValue[0].toInt() and 0xFF) - upperLimit = (validRangeValue[3].toInt() and 0xFF) shl 8 or (validRangeValue[2].toInt() and 0xFF) + lowerLimit = + (validRangeValue[1].toInt() and 0xFF) shl 8 or (validRangeValue[0].toInt() and 0xFF) + upperLimit = + (validRangeValue[3].toInt() and 0xFF) shl 8 or (validRangeValue[2].toInt() and 0xFF) RangeTestValues.setChannelsMinMax(lowerLimit, upperLimit) - val gattCharacteristic1 = GattCharacteristic.fromUuid(descriptor.characteristic.uuid) + val gattCharacteristic1 = + GattCharacteristic.fromUuid(descriptor.characteristic.uuid) gattCharacteristic1?.let { updatePresenter(gatt, descriptor, it) } - }catch (e:Exception) - { + } catch (e: Exception) { e.stackTrace } } @@ -1221,8 +1226,8 @@ class RangeTestActivity : BaseDemoActivity(), Controller { } private fun selectTab(tvTab: TextView) { - setTabUnselected(tv_device1_tab) - setTabUnselected(tv_device2_tab) + setTabUnselected(binding.tvDevice1Tab) + setTabUnselected(binding.tvDevice2Tab) setTabSelected(tvTab) } @@ -1244,7 +1249,7 @@ class RangeTestActivity : BaseDemoActivity(), Controller { android.R.id.home -> { service?.disconnectAllGatts() gatt?.disconnect() - Handler(Looper.getMainLooper()).postDelayed({ onBackPressed() },500) + Handler(Looper.getMainLooper()).postDelayed({ onBackPressed() }, 500) true } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/dialogs/RangeTestModeDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/dialogs/RangeTestModeDialog.kt index b14a8594..4d523ac6 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/dialogs/RangeTestModeDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/dialogs/RangeTestModeDialog.kt @@ -9,12 +9,12 @@ import android.view.View import android.view.ViewGroup import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment +import com.siliconlabs.bledemo.databinding.DialogRangeTestModeBinding import com.siliconlabs.bledemo.features.demo.range_test.activities.RangeTestActivity import com.siliconlabs.bledemo.features.demo.range_test.models.RangeTestMode import com.siliconlabs.bledemo.features.demo.range_test.models.TxPower import com.siliconlabs.bledemo.features.demo.range_test.presenters.RangeTestPresenter.Controller import com.siliconlabs.bledemo.features.demo.range_test.presenters.RangeTestPresenter.RangeTestView -import kotlinx.android.synthetic.main.dialog_range_test_mode.* import java.text.DecimalFormat import java.util.* @@ -22,10 +22,11 @@ import java.util.* * @author Comarch S.A. */ class RangeTestModeDialog : BaseDialogFragment( - hasCustomWidth = true, - isCanceledOnTouchOutside = false + hasCustomWidth = true, + isCanceledOnTouchOutside = false ), RangeTestView { lateinit var controller: Controller + private lateinit var binding: DialogRangeTestModeBinding override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return object : Dialog(requireContext(), theme) { @@ -44,8 +45,13 @@ class RangeTestModeDialog : BaseDialogFragment( setStyle(STYLE_NO_TITLE, theme) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_range_test_mode, container, false) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogRangeTestModeBinding.inflate(inflater, container, false) + return binding.root } @@ -54,16 +60,13 @@ class RangeTestModeDialog : BaseDialogFragment( controller = activity as RangeTestActivity controller.setView(this) - - tx_mode.setOnClickListener { + binding.txMode.setOnClickListener { onTxModeButtonClicked() } - - rx_mode.setOnClickListener { + binding.rxMode.setOnClickListener { onRxModeButtonClicked() } - - btn_cancel.setOnClickListener { + binding.btnCancel.setOnClickListener { onCancelButtonClicked() } @@ -92,17 +95,17 @@ class RangeTestModeDialog : BaseDialogFragment( } override fun showDeviceName(name: String?) { - tv_device_name.text = name + binding.tvDeviceName.text = name } override fun showModelNumber(number: String?, running: Boolean?) { - tv_device_number.text = number + binding.tvDeviceNumber .text = number } override fun showTxPower(power: TxPower?, values: List) { val value = power?.asDisplayValue() val formatter = DecimalFormat("#.##") - tv_tx_power.text = String.format(Locale.ROOT, "%sdBm", formatter.format(value?.toDouble())) + binding.tvTxPower.text = String.format(Locale.ROOT, "%sdBm", formatter.format(value?.toDouble())) } override fun showPayloadLength(length: Int, values: List) { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/fragments/RangeTestFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/fragments/RangeTestFragment.kt index f9e1513f..c09649d1 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/fragments/RangeTestFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/fragments/RangeTestFragment.kt @@ -25,13 +25,13 @@ import com.github.mikephil.charting.utils.Transformer import com.github.mikephil.charting.utils.Utils import com.github.mikephil.charting.utils.ViewPortHandler import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.FragmentRangeTestBinding import com.siliconlabs.bledemo.features.demo.range_test.activities.RangeTestActivity import com.siliconlabs.bledemo.features.demo.range_test.models.RangeTestMode import com.siliconlabs.bledemo.features.demo.range_test.models.RangeTestValues import com.siliconlabs.bledemo.features.demo.range_test.models.TxPower import com.siliconlabs.bledemo.features.demo.range_test.presenters.RangeTestPresenter import com.siliconlabs.bledemo.features.demo.range_test.presenters.RangeTestPresenter.Controller -import kotlinx.android.synthetic.main.fragment_range_test.* import java.util.* import java.util.regex.Pattern @@ -59,6 +59,7 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { private var txLayouts: ArrayList = ArrayList() private var rxLayouts: ArrayList = ArrayList() private var disabledLayouts: ArrayList = ArrayList() + private lateinit var binding: FragmentRangeTestBinding override fun onCreate(savedInstanceState: Bundle?) { @@ -70,60 +71,66 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_range_test, container, false) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentRangeTestBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) txLayouts.apply { - add(range_transmitted_layout) - add(tv_range_tx_power_layout_1) - add(range_tx_power_layout_2) + add(binding.rangeTransmittedLayout) + add(binding.tvRangeTxPowerLayout1) + add(binding.rangeTxPowerLayout2) } rxLayouts.apply { - add(range_rx_data_row_1) - add(range_rx_data_row_2) - add(chart) - add(tv_range_rx_chart_label) + add(binding.rangeRxDataRow1) + add(binding.rangeRxDataRow2) + add(binding.chart) + add(binding.tvRangeRxChartLabel) } disabledLayouts.apply { - add(sb_range_tx_power) - add(sb_range_payload_length) - add(range_seek_ma_window_size) - add(sp_tx_power) - add(sp_payload_length) - add(sp_ma_window_size) - add(sp_remote_id) - add(sp_self_id) - add(range_check_uart_log) - add(range_check_packet_repeat) - add(sp_phy_config) + add(binding.sbRangeTxPower) + add(binding.sbRangePayloadLength) + add(binding.rangeSeekMaWindowSize) + add(binding.spTxPower) + add(binding.spPayloadLength) + add(binding.spMaWindowSize) + add(binding.spRemoteId) + add(binding.spSelfId) + add(binding.rangeCheckUartLog) + add(binding.rangeCheckPacketRepeat) + add(binding.spPhyConfig) } mode?.let { setup(it) } controller.setView(this) - range_test_start_stop.setOnClickListener { + binding.rangeTestStartStop.setOnClickListener { controller.toggleRunningState() } - range_check_packet_repeat.setOnCheckedChangeListener { _, checked -> - sp_packet_count.isEnabled = !checked + binding.rangeCheckPacketRepeat.setOnCheckedChangeListener { _, checked -> + + binding.spPacketCount.isEnabled = !checked if (checked) { controller.updatePacketCount(RangeTestValues.PACKET_COUNT_REPEAT) } else { - val packetCountIndex = sp_packet_count.selectedItemPosition + val packetCountIndex = binding.spPacketCount.selectedItemPosition val packetCount = RangeTestValues.PACKET_COUNT_LOOKUP[packetCountIndex] controller.updatePacketCount(packetCount) } } - range_check_uart_log.setOnCheckedChangeListener { _, checked -> + binding.rangeCheckUartLog.setOnCheckedChangeListener { _, checked -> controller.updateUartLogEnabled(checked) } @@ -136,7 +143,7 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { } private fun clearChart() { - if (chart == null || chartDataSet == null) { + if (binding.chart == null || chartDataSet == null) { return } @@ -148,8 +155,8 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { } chartData?.notifyDataChanged() - chart.notifyDataSetChanged() - chart.invalidate() + binding.chart.notifyDataSetChanged() + binding.chart.invalidate() } override fun runOnUiThread(runnable: Runnable?) { @@ -157,75 +164,79 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { } override fun showDeviceName(name: String?) { - tv_range_device_name.text = name + binding.tvRangeDeviceName.text = name } override fun showModelNumber(number: String?, running: Boolean?) { - tv_range_model_number.text = number + binding.tvRangeModelNumber.text = number val pattern = Pattern.compile(SERIES_2_REGEX, Pattern.CASE_INSENSITIVE) series1 = !pattern.matcher(number).find() - if (running != null && running) sp_channel_number.isEnabled = false - else sp_channel_number.isEnabled = series1 + + if (running != null && running) binding.spChannelNumber.isEnabled = false + else binding.spChannelNumber.isEnabled = series1 } override fun showTxPower(power: TxPower?, values: List) { if (values != txPowerValues) { - setupSpinnerValues(sp_tx_power, values) - sb_range_tx_power.max = values.size - 1 - sb_range_tx_power.setOnSeekBarChangeListener(seekBarsListener) - sp_tx_power.onItemSelectedListener = SpinnerSeekBarListener(sb_range_tx_power) + setupSpinnerValues(binding.spTxPower, values) + binding.sbRangeTxPower.max = values.size - 1 + binding.sbRangeTxPower.setOnSeekBarChangeListener(seekBarsListener) + binding.spTxPower.onItemSelectedListener = + SpinnerSeekBarListener(binding.sbRangeTxPower) txPowerValues = values } val index = values.indexOf(power) if (index != -1) { - sb_range_tx_power.progress = index - sp_tx_power.setSelection(index) + binding.sbRangeTxPower.progress = index + binding.spTxPower.setSelection(index) } } override fun showPayloadLength(length: Int, values: List) { if (values != payloadLengthValues) { - setupSpinnerValues(sp_payload_length, values) - sb_range_payload_length.max = values.size - 1 - sb_range_payload_length.setOnSeekBarChangeListener(seekBarsListener) - sp_payload_length.onItemSelectedListener = SpinnerSeekBarListener(sb_range_payload_length) + setupSpinnerValues(binding.spPayloadLength, values) + binding.sbRangePayloadLength.max = values.size - 1 + binding.sbRangePayloadLength.setOnSeekBarChangeListener(seekBarsListener) + binding.spPayloadLength.onItemSelectedListener = + SpinnerSeekBarListener(binding.sbRangePayloadLength) payloadLengthValues = values } val index = values.indexOf(length) if (index != -1) { - sb_range_payload_length.progress = index - sp_payload_length.setSelection(index) + binding.sbRangePayloadLength.progress = index + binding.spPayloadLength.setSelection(index) } } override fun showMaWindowSize(size: Int, values: List) { if (values != maWindowSizeValues) { - setupSpinnerValues(sp_ma_window_size, values) - range_seek_ma_window_size.max = values.size - 1 - range_seek_ma_window_size.setOnSeekBarChangeListener(seekBarsListener) - sp_ma_window_size.onItemSelectedListener = SpinnerSeekBarListener(range_seek_ma_window_size) + setupSpinnerValues(binding.spMaWindowSize, values) + binding.rangeSeekMaWindowSize.max = values.size - 1 + binding.rangeSeekMaWindowSize.setOnSeekBarChangeListener(seekBarsListener) + binding.spMaWindowSize.onItemSelectedListener = + SpinnerSeekBarListener(binding.rangeSeekMaWindowSize) maWindowSizeValues = values } val index = values.indexOf(size) if (index != -1) { - range_seek_ma_window_size.progress = index - sp_ma_window_size.setSelection(index) + binding.rangeSeekMaWindowSize.progress = index + binding.spMaWindowSize.setSelection(index) } } override fun showChannelNumber(number: Int) { val index = RangeTestValues.CHANNEL_LOOKUP.indexOf(number) - sp_channel_number.setSelection(index) + binding.spChannelNumber.setSelection(index) } override fun showPacketCountRepeat(enabled: Boolean) { - range_check_packet_repeat.isChecked = enabled + binding.rangeCheckPacketRepeat.isChecked = enabled } override fun showPacketRequired(required: Int) { val index = RangeTestValues.PACKET_COUNT_LOOKUP.indexOf(required) - sp_packet_count.setSelection(index) + binding.spPacketCount.setSelection(index) } override fun showPacketSent(sent: Int) { @@ -242,16 +253,16 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { override fun showRemoteId(id: Int) { val index = RangeTestValues.ID_LOOKUP.indexOf(id) - sp_remote_id.setSelection(index) + binding.spRemoteId.setSelection(index) } override fun showSelfId(id: Int) { val index = RangeTestValues.ID_LOOKUP.indexOf(id) - sp_self_id.setSelection(index) + binding.spSelfId.setSelection(index) } override fun showUartLogEnabled(enabled: Boolean) { - range_check_uart_log.isChecked = enabled + binding.rangeCheckUartLog.isChecked = enabled } override fun showRunningState(running: Boolean) { @@ -272,16 +283,17 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { return } - val valuesList: List> = ArrayList>(values.entries) + val valuesList: List> = + ArrayList>(values.entries) if (valuesList != phyValues) { - setupSpinnerValues(sp_phy_config, ArrayList(values.values)) + setupSpinnerValues(binding.spPhyConfig, ArrayList(values.values)) phyValues = valuesList } for (i in valuesList.indices) { val entry = valuesList[i] if (entry.key == phy) { - sp_phy_config.setSelection(i) + binding.spPhyConfig.setSelection(i) return } } @@ -296,7 +308,7 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { seekBarsListener = SeekBarsListener() if (mode == RangeTestMode.Tx) { setVisibility(rxLayouts, View.GONE) - setupSpinnerValues(sp_tx_power, listOf("")) + setupSpinnerValues(binding.spTxPower, listOf("")) buttonStringIdOff = R.string.range_tx_start buttonStringIdOn = R.string.range_tx_stop setPacketSent(0) @@ -311,93 +323,96 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { setPer(0f) } - setupSpinnerValues(sp_payload_length, listOf("")) - setupSpinnerValues(sp_ma_window_size, listOf("")) - setupSpinnerValues(sp_channel_number, RangeTestValues.CHANNEL_LOOKUP) - setupSpinnerValues(sp_packet_count, RangeTestValues.PACKET_COUNT_LOOKUP) - setupSpinnerValues(sp_remote_id, RangeTestValues.ID_LOOKUP) - setupSpinnerValues(sp_self_id, RangeTestValues.ID_LOOKUP) + setupSpinnerValues(binding.spPayloadLength, listOf("")) + setupSpinnerValues(binding.spMaWindowSize, listOf("")) + setupSpinnerValues(binding.spChannelNumber, RangeTestValues.CHANNEL_LOOKUP) + setupSpinnerValues(binding.spPacketCount, RangeTestValues.PACKET_COUNT_LOOKUP) + setupSpinnerValues(binding.spRemoteId, RangeTestValues.ID_LOOKUP) + setupSpinnerValues(binding.spSelfId, RangeTestValues.ID_LOOKUP) - sp_channel_number.onItemSelectedListener = object : SpinnerListener() { + binding.spChannelNumber.onItemSelectedListener = object : SpinnerListener() { override fun onItemSelected(index: Int) { val channel = RangeTestValues.CHANNEL_LOOKUP[index] controller.updateChannel(channel) } } - sp_packet_count.onItemSelectedListener = object : SpinnerListener() { + binding.spPacketCount.onItemSelectedListener = object : SpinnerListener() { override fun onItemSelected(index: Int) { val packetCount = RangeTestValues.PACKET_COUNT_LOOKUP[index] controller.updatePacketCount(packetCount) } } - sp_remote_id.onItemSelectedListener = object : SpinnerListener() { + binding.spRemoteId.onItemSelectedListener = object : SpinnerListener() { override fun onItemSelected(index: Int) { val id = RangeTestValues.ID_LOOKUP[index] controller.updateRemoteId(id) } } - sp_self_id.onItemSelectedListener = object : SpinnerListener() { + binding.spSelfId.onItemSelectedListener = object : SpinnerListener() { override fun onItemSelected(index: Int) { val id = RangeTestValues.ID_LOOKUP[index] controller.updateSelfId(id) } } - sp_phy_config.onItemSelectedListener = object : SpinnerListener() { + binding.spPhyConfig.onItemSelectedListener = object : SpinnerListener() { override fun onItemSelected(index: Int) { val id = phyValues!![index].key controller.updatePhyConfig(id) } } - sb_range_tx_power.isEnabled = false - sp_tx_power.isEnabled = false - sb_range_payload_length.isEnabled = false - sp_payload_length.isEnabled = false - range_seek_ma_window_size.isEnabled = false - sp_ma_window_size.isEnabled = false - sp_phy_config.isEnabled = false + binding.sbRangeTxPower.isEnabled = false + binding.spTxPower.isEnabled = false + binding.sbRangePayloadLength.isEnabled = false + binding.spPayloadLength.isEnabled = false + binding.rangeSeekMaWindowSize.isEnabled = false + binding.spMaWindowSize.isEnabled = false + binding.spPhyConfig.isEnabled = false setupRunning(false) } private fun appendChartData(rssi: Float) { - if (chart == null || chartDataSet == null) { + if (binding.chart == null || chartDataSet == null) { return } val index = chartDataSet!!.entryCount chartDataSet!!.addEntry(Entry(index.toFloat(), rssi)) - chart.xAxis.axisMinimum = 0f - chart.xAxis.axisMaximum = Math.max(50, index).toFloat() - chart.setVisibleXRange(50f, 50f) - chart.moveViewToX(index.toFloat()) + binding.chart.xAxis.axisMinimum = 0f + binding.chart.xAxis.axisMaximum = Math.max(50, index).toFloat() + binding.chart.setVisibleXRange(50f, 50f) + binding.chart.moveViewToX(index.toFloat()) chartDataSet?.notifyDataSetChanged() chartData?.notifyDataChanged() - chart.notifyDataSetChanged() - chart.invalidate() + binding.chart.notifyDataSetChanged() + binding.chart.invalidate() } private fun setPacketSent(packetSent: Int) { - tv_range_test_packet_count.text = packetSent.toString() + binding.tvRangeTestPacketCount.text = packetSent.toString() } private fun setRx(received: Int, required: Int) { - setValue(tv_range_test_rx, R.string.range_rx_rx, received, required) + + setValue(binding.tvRangeTestRx, R.string.range_rx_rx, received, required) } private fun setRssi(rssi: Int) { - setValue(tv_range_test_rssi, R.string.range_rx_rssi, rssi) + + setValue(binding.tvRangeTestRssi, R.string.range_rx_rssi, rssi) } private fun setMa(ma: Float) { - tv_range_test_ma.text = String.format(Locale.US,"%.1f",ma).plus("%") + + binding.tvRangeTestMa.text = String.format(Locale.US, "%.1f", ma).plus("%") } private fun setPer(per: Float) { - tv_range_test_ma.text = String.format(Locale.US,"%.1f",per).plus("%") + binding.tvRangeTestMa.text = String.format(Locale.US, "%.1f", per).plus("%") } private fun setValue(view: TextView, resId: Int, vararg args: Any) { @@ -407,15 +422,17 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { private fun updateControllerFromSeekBar(seekBar: SeekBar) { val progress = seekBar.progress when { - seekBar === sb_range_tx_power -> { + seekBar === binding.sbRangeTxPower -> { val power = txPowerValues!![progress] controller.updateTxPower(power.asCharacteristicValue()) } - seekBar === sb_range_payload_length -> { + + seekBar === binding.sbRangePayloadLength -> { val payloadLength = payloadLengthValues!![progress] controller.updatePayloadLength(payloadLength) } - seekBar === range_seek_ma_window_size -> { + + seekBar === binding.rangeSeekMaWindowSize -> { val maWindowSize = maWindowSizeValues!![progress] controller.updateMaWindowSize(maWindowSize) } @@ -425,16 +442,16 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { private fun setupRunning(running: Boolean) { if (running) { setEnabled(disabledLayouts, false) - sp_channel_number.isEnabled = false - sp_packet_count.isEnabled = false - range_test_start_stop.setText(buttonStringIdOn) - range_test_start_stop.isEnabled = mode != RangeTestMode.Rx + binding.spChannelNumber.isEnabled = false + binding.spPacketCount.isEnabled = false + binding.rangeTestStartStop.setText(buttonStringIdOn) + binding.rangeTestStartStop.isEnabled = mode != RangeTestMode.Rx } else { setEnabled(disabledLayouts, true) - sp_channel_number.isEnabled = series1 - sp_packet_count.isEnabled = !range_check_packet_repeat.isChecked - range_test_start_stop.setText(buttonStringIdOff) - range_test_start_stop.isEnabled = true + binding.spChannelNumber.isEnabled = series1 + binding.spPacketCount.isEnabled = !binding.rangeCheckPacketRepeat.isChecked + binding.rangeTestStartStop.setText(buttonStringIdOff) + binding.rangeTestStartStop.isEnabled = true } } @@ -462,7 +479,7 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { val axisColor = ContextCompat.getColor(requireContext(), R.color.silabs_dark_gray_text) val graphColor = ContextCompat.getColor(requireContext(), R.color.silabs_blue) - chart.apply { + binding.chart.apply { description?.isEnabled = false legend?.isEnabled = false setScaleEnabled(false) @@ -480,7 +497,11 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { axisLeft.setDrawAxisLine(true) axisLeft.axisLineWidth = 0.5f axisLeft.axisLineColor = axisColor - rendererLeftYAxis = YAxisArrowRenderer(chart.viewPortHandler, chart.axisLeft, chart.rendererLeftYAxis.transformer) + rendererLeftYAxis = YAxisArrowRenderer( + binding.chart.viewPortHandler, + binding.chart.axisLeft, + binding.chart.rendererLeftYAxis.transformer + ) xAxis.setDrawGridLines(false) xAxis.setDrawAxisLine(false) xAxis.axisMinimum = 0f @@ -497,11 +518,11 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { } val chartData = createChartData(graphColor) - chart.data = chartData + binding.chart.data = chartData chartDataSet!!.notifyDataSetChanged() chartData.notifyDataChanged() - chart.notifyDataSetChanged() - chart.invalidate() + binding.chart.notifyDataSetChanged() + binding.chart.invalidate() } private fun createChartData(color: Int): LineData { @@ -550,14 +571,16 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { if (fromUser) { when { - seekBar === sb_range_tx_power -> { - sp_tx_power.setSelection(progress) + seekBar === binding.sbRangeTxPower -> { + binding.spTxPower.setSelection(progress) } - seekBar === sb_range_payload_length -> { - sp_payload_length.setSelection(progress) + + seekBar === binding.sbRangePayloadLength -> { + binding.spPayloadLength.setSelection(progress) } - seekBar === range_seek_ma_window_size -> { - sp_ma_window_size.setSelection(progress) + + seekBar === binding.rangeSeekMaWindowSize -> { + binding.spMaWindowSize.setSelection(progress) } } } @@ -569,7 +592,8 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { } } - private inner class SpinnerSeekBarListener(private val seekBar: SeekBar) : AdapterView.OnItemSelectedListener { + private inner class SpinnerSeekBarListener(private val seekBar: SeekBar) : + AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) { seekBar.progress = position updateControllerFromSeekBar(seekBar) @@ -601,12 +625,19 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { } private inner class CubicLineSampleFillFormatter : IFillFormatter { - override fun getFillLinePosition(dataSet: ILineDataSet, dataProvider: LineDataProvider): Float { + override fun getFillLinePosition( + dataSet: ILineDataSet, + dataProvider: LineDataProvider + ): Float { return (-100).toFloat() } } - private class YAxisArrowRenderer(viewPortHandler: ViewPortHandler, yAxis: YAxis, trans: Transformer) : YAxisRenderer(viewPortHandler, yAxis, trans) { + private class YAxisArrowRenderer( + viewPortHandler: ViewPortHandler, + yAxis: YAxis, + trans: Transformer + ) : YAxisRenderer(viewPortHandler, yAxis, trans) { private val ARROW_SIZE = Utils.convertDpToPixel(3.5f) private val arrowPath = Path() private val arrowPaint = Paint(Paint.ANTI_ALIAS_FLAG) @@ -622,18 +653,46 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { arrowPaint.color = mYAxis.axisLineColor arrowPath.reset() if (mYAxis.axisDependency == YAxis.AxisDependency.LEFT) { - c.drawLine(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop() - ARROW_SIZE * 1.5f, mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom(), mAxisLinePaint) - arrowPath.moveTo(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop() - ARROW_SIZE * 1.5f) - arrowPath.lineTo(mViewPortHandler.contentLeft() + ARROW_SIZE / 2f, mViewPortHandler.contentTop() - ARROW_SIZE * 0.5f) - arrowPath.lineTo(mViewPortHandler.contentLeft() - ARROW_SIZE / 2f, mViewPortHandler.contentTop() - ARROW_SIZE * 0.5f) + c.drawLine( + mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop() - ARROW_SIZE * 1.5f, + mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), + mAxisLinePaint + ) + arrowPath.moveTo( + mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop() - ARROW_SIZE * 1.5f + ) + arrowPath.lineTo( + mViewPortHandler.contentLeft() + ARROW_SIZE / 2f, + mViewPortHandler.contentTop() - ARROW_SIZE * 0.5f + ) + arrowPath.lineTo( + mViewPortHandler.contentLeft() - ARROW_SIZE / 2f, + mViewPortHandler.contentTop() - ARROW_SIZE * 0.5f + ) arrowPath.close() } else { - c.drawLine(mViewPortHandler.contentRight(), mViewPortHandler.contentTop() - ARROW_SIZE * 1.5f, mViewPortHandler.contentRight(), - mViewPortHandler.contentBottom(), mAxisLinePaint) - arrowPath.moveTo(mViewPortHandler.contentRight(), mViewPortHandler.contentTop() - ARROW_SIZE * 1.5f) - arrowPath.lineTo(mViewPortHandler.contentRight() + ARROW_SIZE / 2f, mViewPortHandler.contentTop() - ARROW_SIZE * 0.5f) - arrowPath.lineTo(mViewPortHandler.contentRight() - ARROW_SIZE / 2f, mViewPortHandler.contentTop() - ARROW_SIZE * 0.5f) + c.drawLine( + mViewPortHandler.contentRight(), + mViewPortHandler.contentTop() - ARROW_SIZE * 1.5f, + mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom(), + mAxisLinePaint + ) + arrowPath.moveTo( + mViewPortHandler.contentRight(), + mViewPortHandler.contentTop() - ARROW_SIZE * 1.5f + ) + arrowPath.lineTo( + mViewPortHandler.contentRight() + ARROW_SIZE / 2f, + mViewPortHandler.contentTop() - ARROW_SIZE * 0.5f + ) + arrowPath.lineTo( + mViewPortHandler.contentRight() - ARROW_SIZE / 2f, + mViewPortHandler.contentTop() - ARROW_SIZE * 0.5f + ) arrowPath.close() } c.drawPath(arrowPath, arrowPaint) @@ -669,8 +728,14 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { arrowPaint.color = l.getLineColor() arrowPath.reset() arrowPath.moveTo(mViewPortHandler.contentRight(), pts[1]) - arrowPath.lineTo(mViewPortHandler.contentRight() - ARROW_SIZE, pts[1] + ARROW_SIZE / 2f) - arrowPath.lineTo(mViewPortHandler.contentRight() - ARROW_SIZE, pts[1] - ARROW_SIZE / 2f) + arrowPath.lineTo( + mViewPortHandler.contentRight() - ARROW_SIZE, + pts[1] + ARROW_SIZE / 2f + ) + arrowPath.lineTo( + mViewPortHandler.contentRight() - ARROW_SIZE, + pts[1] - ARROW_SIZE / 2f + ) arrowPath.close() c.drawPath(arrowPath, arrowPaint) } @@ -690,24 +755,32 @@ class RangeTestFragment : Fragment(), RangeTestPresenter.RangeTestView { val position = l.labelPosition if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) { mLimitLinePaint.textAlign = Paint.Align.RIGHT - c.drawText(label, - mViewPortHandler.contentRight() - xOffset, - pts[1] - yOffset + labelLineHeight, mLimitLinePaint) + c.drawText( + label, + mViewPortHandler.contentRight() - xOffset, + pts[1] - yOffset + labelLineHeight, mLimitLinePaint + ) } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { mLimitLinePaint.textAlign = Paint.Align.RIGHT - c.drawText(label, - mViewPortHandler.contentRight() - xOffset, - pts[1] + yOffset, mLimitLinePaint) + c.drawText( + label, + mViewPortHandler.contentRight() - xOffset, + pts[1] + yOffset, mLimitLinePaint + ) } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) { mLimitLinePaint.textAlign = Paint.Align.LEFT - c.drawText(label, - mViewPortHandler.contentLeft() + xOffset, - pts[1] - yOffset + labelLineHeight, mLimitLinePaint) + c.drawText( + label, + mViewPortHandler.contentLeft() + xOffset, + pts[1] - yOffset + labelLineHeight, mLimitLinePaint + ) } else { mLimitLinePaint.textAlign = Paint.Align.LEFT - c.drawText(label, - mViewPortHandler.offsetLeft() + xOffset, - pts[1] + yOffset, mLimitLinePaint) + c.drawText( + label, + mViewPortHandler.offsetLeft() + xOffset, + pts[1] + yOffset, mLimitLinePaint + ) } } c.restoreToCount(clipRestoreCount) diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/activities/ThunderboardActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/activities/ThunderboardActivity.kt index 7bdf1cd6..1d53e25b 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/activities/ThunderboardActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/activities/ThunderboardActivity.kt @@ -3,17 +3,20 @@ package com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.activities import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothProfile import android.os.Bundle +import android.view.LayoutInflater import android.widget.FrameLayout +import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.base.activities.BaseDemoActivity import com.siliconlabs.bledemo.bluetooth.ble.GattCharacteristic import com.siliconlabs.bledemo.bluetooth.ble.GattService import com.siliconlabs.bledemo.bluetooth.ble.TimeoutGattCallback -import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.ActivityThunderboardBaseBinding import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.fragments.StatusFragment -import com.siliconlabs.bledemo.base.activities.BaseDemoActivity import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.models.ThunderBoardDevice import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.utils.SensorChecker import com.siliconlabs.bledemo.utils.GattQueue -import kotlinx.android.synthetic.main.activity_thunderboard_base.* + +//import kotlinx.android.synthetic.main.activity_thunderboard_base.* abstract class ThunderboardActivity : BaseDemoActivity() { @@ -25,12 +28,17 @@ abstract class ThunderboardActivity : BaseDemoActivity() { protected var mainSection: FrameLayout? = null protected lateinit var statusFragment: StatusFragment + private lateinit var binding: ActivityThunderboardBaseBinding public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_thunderboard_base) - mainSection = main_section as FrameLayout - statusFragment = supportFragmentManager.findFragmentById(R.id.bluegecko_status_fragment) as StatusFragment + + binding = ActivityThunderboardBaseBinding.inflate(LayoutInflater.from(this)) + setContentView(binding.root) + + mainSection = binding.mainSection as FrameLayout + statusFragment = + supportFragmentManager.findFragmentById(R.id.bluegecko_status_fragment) as StatusFragment } override fun onBluetoothServiceBound() { @@ -47,18 +55,51 @@ abstract class ThunderboardActivity : BaseDemoActivity() { protected fun queueReadingDeviceCharacteristics() { gattQueue.let { if (statusFragment.viewModel.thunderboardDevice.value?.name == null) { - it.queueRead(getDeviceCharacteristic(GattService.GenericAccess, GattCharacteristic.DeviceName)) + it.queueRead( + getDeviceCharacteristic( + GattService.GenericAccess, + GattCharacteristic.DeviceName + ) + ) } - it.queueRead(getDeviceCharacteristic(GattService.DeviceInformation, GattCharacteristic.ModelNumberString)) - it.queueRead(getDeviceCharacteristic(GattService.BatteryService, GattCharacteristic.BatteryLevel)) - it.queueRead(getDeviceCharacteristic(GattService.PowerSource, GattCharacteristic.PowerSource)) - it.queueRead(getDeviceCharacteristic(GattService.DeviceInformation, GattCharacteristic.FirmwareRevision)) + it.queueRead( + getDeviceCharacteristic( + GattService.DeviceInformation, + GattCharacteristic.ModelNumberString + ) + ) + it.queueRead( + getDeviceCharacteristic( + GattService.BatteryService, + GattCharacteristic.BatteryLevel + ) + ) + it.queueRead( + getDeviceCharacteristic( + GattService.PowerSource, + GattCharacteristic.PowerSource + ) + ) + it.queueRead( + getDeviceCharacteristic( + GattService.DeviceInformation, + GattCharacteristic.FirmwareRevision + ) + ) - it.queueNotify(getDeviceCharacteristic(GattService.BatteryService, GattCharacteristic.BatteryLevel)) + it.queueNotify( + getDeviceCharacteristic( + GattService.BatteryService, + GattCharacteristic.BatteryLevel + ) + ) } } - private fun getDeviceCharacteristic(gattService: GattService, characteristic: GattCharacteristic): BluetoothGattCharacteristic? { + private fun getDeviceCharacteristic( + gattService: GattService, + characteristic: GattCharacteristic + ): BluetoothGattCharacteristic? { return gatt?.getService(gattService.number)?.getCharacteristic(characteristic.uuid) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/fragments/StatusFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/fragments/StatusFragment.kt index 064f4b6c..0e5f46ef 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/fragments/StatusFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/fragments/StatusFragment.kt @@ -15,16 +15,18 @@ import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import com.siliconlabs.bledemo.bluetooth.ble.GattCharacteristic import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.FragmentDemoBinding +import com.siliconlabs.bledemo.databinding.FragmentDeviceStatusBinding import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.activities.ThunderboardActivity import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.viewmodels.StatusViewModel import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.models.ThunderBoardDevice -import kotlinx.android.synthetic.main.fragment_device_status.view.* + class StatusFragment : Fragment() { lateinit var viewModel: StatusViewModel - private lateinit var rootView: View + private lateinit var rootView:FragmentDeviceStatusBinding private var isConnecting = false override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, @@ -34,10 +36,11 @@ class StatusFragment : Fragment() { setupDataListeners() - rootView = inflater.inflate(R.layout.fragment_device_status, container, false) - rootView.battery_indicator.visibility = View.INVISIBLE - rootView.battery_indicator.setBatteryValue(ThunderBoardDevice.PowerSource.UNKNOWN, 0) - return rootView + rootView = FragmentDeviceStatusBinding.inflate(inflater, container, false) + + rootView.batteryIndicator.visibility = View.INVISIBLE + rootView.batteryIndicator.setBatteryValue(ThunderBoardDevice.PowerSource.UNKNOWN, 0) + return rootView.root } fun handleBaseCharacteristic(characteristic: BluetoothGattCharacteristic) { @@ -70,12 +73,13 @@ class StatusFragment : Fragment() { private fun updateStatusViews(device: ThunderBoardDevice) { rootView.apply { - device_name.text = device.name - device_firmware.text = + + deviceName.text = device.name + deviceFirmware.text = if (device.firmwareVersion == null || device.firmwareVersion?.isEmpty()!!) { getString(R.string.status_no_firmware_version) } else device.firmwareVersion - battery_indicator.setBatteryValue(device.powerSource, device.batteryLevel) + batteryIndicator.setBatteryValue(device.powerSource, device.batteryLevel) } } @@ -85,20 +89,20 @@ class StatusFragment : Fragment() { BluetoothProfile.STATE_CONNECTED -> { resourceId = R.string.status_connected isConnecting = false - rootView.battery_indicator.visibility = View.VISIBLE - rootView.progress_bar.visibility = View.INVISIBLE + rootView.batteryIndicator.visibility = View.VISIBLE + rootView.progressBar.visibility = View.INVISIBLE } BluetoothProfile.STATE_CONNECTING -> { resourceId = R.string.status_connecting isConnecting = true - rootView.battery_indicator.visibility = View.INVISIBLE - rootView.progress_bar.visibility = View.VISIBLE + rootView.batteryIndicator.visibility = View.INVISIBLE + rootView.progressBar.visibility = View.VISIBLE } BluetoothProfile.STATE_DISCONNECTING -> { resourceId = BluetoothProfile.STATE_DISCONNECTING isConnecting = false - rootView.battery_indicator.visibility = View.INVISIBLE - rootView.progress_bar.visibility = View.VISIBLE + rootView.batteryIndicator.visibility = View.INVISIBLE + rootView.progressBar.visibility = View.VISIBLE } else -> { val titleId: Int @@ -112,13 +116,13 @@ class StatusFragment : Fragment() { } resourceId = R.string.status_disconnected isConnecting = false - rootView.battery_indicator.visibility = View.INVISIBLE - rootView.progress_bar.visibility = View.VISIBLE + rootView.batteryIndicator.visibility = View.INVISIBLE + rootView.progressBar.visibility = View.VISIBLE animateDown() showNotConnectedDialog(viewModel.thunderboardDevice.value?.name, titleId, messageId) } } - rootView.device_status.text = getString(resourceId) + rootView.deviceStatus.text = getString(resourceId) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/ui/BatteryIndicator.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/ui/BatteryIndicator.kt index db6f5fc3..8c88cdbd 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/ui/BatteryIndicator.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/ui/BatteryIndicator.kt @@ -5,35 +5,42 @@ import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.BatteryIndicatorBinding import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.models.ThunderBoardDevice -import kotlinx.android.synthetic.main.battery_indicator.view.* -class BatteryIndicator @JvmOverloads constructor(context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 +//import kotlinx.android.synthetic.main.battery_indicator.view.* + +class BatteryIndicator @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 ) : FrameLayout(context, attrs, defStyleAttr) { + private lateinit var binding: BatteryIndicatorBinding fun setBatteryValue(powerSource: ThunderBoardDevice.PowerSource, batteryValue: Int) { when (powerSource) { ThunderBoardDevice.PowerSource.UNKNOWN -> { - battery_percent.setText(R.string.unknown_power) - battery_meter.setImageResource(R.drawable.icn_signal_unknown) + + binding.batteryPercent.setText(R.string.unknown_power) + binding.batteryMeter.setImageResource(R.drawable.icn_signal_unknown) } + ThunderBoardDevice.PowerSource.USB -> { - battery_percent.setText(R.string.usb_power) - battery_meter.setImageResource(R.drawable.icon_usb) + binding.batteryPercent.setText(R.string.usb_power) + binding.batteryMeter.setImageResource(R.drawable.icon_usb) } + else -> { - battery_percent.text = String.format("%d%%", batteryValue) - battery_meter.setValue(batteryValue) + binding.batteryPercent.text = String.format("%d%%", batteryValue) + binding.batteryMeter.setValue(batteryValue) } } } init { val inflater = LayoutInflater.from(context) - val view = inflater.inflate(R.layout.battery_indicator, this, false) - addView(view) + binding = BatteryIndicatorBinding.inflate(inflater, this, false) + addView(binding.root) } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/activities/BlinkyThunderboardActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/activities/BlinkyThunderboardActivity.kt index fbc1114b..c12dc1f2 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/activities/BlinkyThunderboardActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/activities/BlinkyThunderboardActivity.kt @@ -1,5 +1,6 @@ package com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.blinky_thunderboard.activities +//import kotlinx.android.synthetic.main.activity_blinky_thunderboard.* import android.annotation.SuppressLint import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic @@ -10,18 +11,17 @@ import android.view.View import androidx.cardview.widget.CardView import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider -import com.siliconlabs.bledemo.home_screen.dialogs.SelectDeviceDialog import com.siliconlabs.bledemo.bluetooth.ble.GattCharacteristic import com.siliconlabs.bledemo.bluetooth.ble.GattService import com.siliconlabs.bledemo.bluetooth.ble.TimeoutGattCallback -import com.siliconlabs.bledemo.R -import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.blinky_thunderboard.viewmodels.BlinkyThunderboardViewModel +import com.siliconlabs.bledemo.databinding.ActivityBlinkyThunderboardBinding +import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.activities.ThunderboardActivity +import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.models.ThunderBoardDevice import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.blinky_thunderboard.control.ColorLEDControl import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.blinky_thunderboard.control.ColorLEDControl.ColorLEDControlListener -import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.activities.ThunderboardActivity import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.blinky_thunderboard.model.LedRGBState -import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.models.ThunderBoardDevice -import kotlinx.android.synthetic.main.activity_blinky_thunderboard.* +import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.blinky_thunderboard.viewmodels.BlinkyThunderboardViewModel +import com.siliconlabs.bledemo.home_screen.dialogs.SelectDeviceDialog import java.util.* class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListener { @@ -30,18 +30,20 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen private lateinit var colorLEDControl: ColorLEDControl private lateinit var viewModel: BlinkyThunderboardViewModel + private lateinit var binding:ActivityBlinkyThunderboardBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val view = LayoutInflater.from(this).inflate(R.layout.activity_blinky_thunderboard, null, false) - colorLEDControl = view.findViewById(R.id.color_led_control) // make the view gone (if necessary) before it can be showed - ledsControl = view.findViewById(R.id.leds_control) + binding = ActivityBlinkyThunderboardBinding. inflate(LayoutInflater.from(this), null, false) + + colorLEDControl = binding.colorLedControl // make the view gone (if necessary) before it can be showed + ledsControl = binding.ledsControl val powerSourceIntent = intent.getIntExtra(SelectDeviceDialog.POWER_SOURCE_EXTRA, 0) val modelNumberIntent = intent.getStringExtra(SelectDeviceDialog.MODEL_TYPE_EXTRA) setControlsVisibility(ThunderBoardDevice.PowerSource.fromInt(powerSourceIntent), modelNumberIntent) - mainSection?.addView(view) + mainSection?.addView(binding.root) viewModel = ViewModelProvider(this).get(BlinkyThunderboardViewModel::class.java) @@ -50,20 +52,21 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen } private fun setupUiListeners() { - led_0.setOnCheckedChangeListener { _, isChecked -> + + binding.led0.setOnCheckedChangeListener { _, isChecked -> var action = 0 if (isChecked) action = BlinkyThunderboardViewModel.LED_0_ON - if (led_1.isChecked) action = action or BlinkyThunderboardViewModel.LED_1_ON + if (binding.led1.isChecked) action = action or BlinkyThunderboardViewModel.LED_1_ON getDigitalWriteCharacteristic()?.apply { value = byteArrayOf(action.toByte()) gattQueue.queueWrite(this) } } - led_1.setOnCheckedChangeListener { _, isChecked -> + binding.led1.setOnCheckedChangeListener { _, isChecked -> var action = 0 if (isChecked) action = BlinkyThunderboardViewModel.LED_1_ON - if (led_0.isChecked) action = action or BlinkyThunderboardViewModel.LED_0_ON + if (binding.led0.isChecked) action = action or BlinkyThunderboardViewModel.LED_0_ON getDigitalWriteCharacteristic()?.apply { value = byteArrayOf(action.toByte()) @@ -74,12 +77,13 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen } private fun setupDataListeners(modelNumber: String?) { - viewModel.button0.observe(this, Observer { switch_0.setChecked(it) }) - viewModel.button1.observe(this, Observer { switch_1.setChecked(it) }) + + viewModel.button0.observe(this, Observer { binding.switch0.setChecked(it) }) + viewModel.button1.observe(this, Observer { binding.switch1.setChecked(it) }) viewModel.led0.observe(this, Observer { - if (it != led_0.isChecked) led_0.isChecked = it }) + if (it != binding.led0.isChecked) binding.led0.isChecked = it }) viewModel.led1.observe(this, Observer { - if (it != led_1.isChecked) led_1.isChecked = it }) + if (it != binding.led1.isChecked) binding.led1.isChecked = it }) when (modelNumber) { ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V3, diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/control/ColorLEDControl.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/control/ColorLEDControl.kt index 73241ea1..53a547d0 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/control/ColorLEDControl.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/control/ColorLEDControl.kt @@ -12,15 +12,17 @@ import android.widget.SeekBar import android.widget.SeekBar.OnSeekBarChangeListener import androidx.appcompat.widget.SwitchCompat import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.IodemoColorLedsBinding import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.blinky_thunderboard.model.LedRGBState import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.blinky_thunderboard.ui.ColorLEDs import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.blinky_thunderboard.ui.HueBackgroundView -import kotlinx.android.synthetic.main.iodemo_color_leds.view.* + +//import kotlinx.android.synthetic.main.iodemo_color_leds.view.* class ColorLEDControl @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 ) : FrameLayout(context, attrs, defStyleAttr) { private lateinit var colorLEDs: ColorLEDs @@ -31,29 +33,32 @@ class ColorLEDControl @JvmOverloads constructor( private var hue: Float = 0f // from 0 to 360 private var brightness: Float = 1f // from 0 to 1 + private lateinit var binding: IodemoColorLedsBinding //iodemo_color_leds private var colorLEDControlListener: ColorLEDControlListener? = null - private fun setupViews(rootView: LinearLayout) { - colorLEDs = rootView.iodemo_color_leds - hueSelect = rootView.iodemo_hue_select - brightnessSelect = rootView.iodemo_brightness_select - colorSwitch = rootView.iodemo_color_switch - hueBackgroundView = rootView.iodemo_hue_background + private fun setupViews(rootView: IodemoColorLedsBinding) { + + colorLEDs = rootView.iodemoColorLeds + hueSelect = rootView.iodemoHueSelect + brightnessSelect = rootView.iodemoBrightnessSelect + colorSwitch = rootView.iodemoColorSwitch + hueBackgroundView = rootView.iodemoHueBackground } - private var selectBrightnessListener: OnSeekBarChangeListener = object : OnSeekBarChangeListener { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - brightness = findBrightness(progress) - setColorLEDs(colorSwitch.isChecked, hue, brightness) - colorLEDs.setAlpha(progress) - } + private var selectBrightnessListener: OnSeekBarChangeListener = + object : OnSeekBarChangeListener { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + brightness = findBrightness(progress) + setColorLEDs(colorSwitch.isChecked, hue, brightness) + colorLEDs.setAlpha(progress) + } - override fun onStartTrackingTouch(seekBar: SeekBar) {} - override fun onStopTrackingTouch(seekBar: SeekBar) { - colorLEDControlListener?.onLedUpdateStop() + override fun onStartTrackingTouch(seekBar: SeekBar) {} + override fun onStopTrackingTouch(seekBar: SeekBar) { + colorLEDControlListener?.onLedUpdateStop() + } } - } fun setColorLEDControlListener(listener: ColorLEDControlListener?) { colorLEDControlListener = listener @@ -65,10 +70,11 @@ class ColorLEDControl @JvmOverloads constructor( enableControls(isOn) val hsv = FloatArray(3) Color.RGBToHSV( - colorLEDsValue.red, - colorLEDsValue.green, - colorLEDsValue.blue, - hsv) + colorLEDsValue.red, + colorLEDsValue.green, + colorLEDsValue.blue, + hsv + ) hue = hsv[0] brightness = hsv[2] hueSelect.progress = hue.toInt() @@ -97,12 +103,14 @@ class ColorLEDControl @JvmOverloads constructor( private fun setColorLEDs(switchState: Boolean, hue: Float, brightness: Float) { val color = hsvToRGB(hue, brightness) - colorLEDControlListener?.updateColorLEDs(LedRGBState( + colorLEDControlListener?.updateColorLEDs( + LedRGBState( switchState, Color.red(color), Color.green(color), Color.blue(color) - )) + ) + ) } private fun hsvToRGB(hue: Float, brightness: Float): Int { @@ -127,10 +135,11 @@ class ColorLEDControl @JvmOverloads constructor( } init { - val layout = LayoutInflater.from(context).inflate( - R.layout.iodemo_color_leds, this, false) as LinearLayout +// val layout = LayoutInflater.from(context).inflate( +// R.layout.iodemo_color_leds, this, false) as LinearLayout + val layout = IodemoColorLedsBinding.inflate(LayoutInflater.from(context)) setupViews(layout) - addView(layout) + addView(layout.root) val color = hsvToRGB(hue, brightness) diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/control/SwitchControl.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/control/SwitchControl.kt index 9a38c675..943f463f 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/control/SwitchControl.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/control/SwitchControl.kt @@ -5,26 +5,30 @@ import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.switch_control.view.* +import com.siliconlabs.bledemo.databinding.SwitchControlBinding + +//import kotlinx.android.synthetic.main.switch_control.view.* class SwitchControl @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 ) : FrameLayout(context, attrs, defStyleAttr) { + private lateinit var binding: SwitchControlBinding fun setChecked(checked: Boolean) { - switch_image.setImageResource( - if (checked) R.drawable.switch_status_on - else R.drawable.switch_status_off + binding.switchImage.setImageResource( + if (checked) R.drawable.switch_status_on + else R.drawable.switch_status_off ) - switch_text.text = context.getString(if (checked) R.string.blinky_tb_on else R.string.blinky_tb_off) + binding.switchText.text = + context.getString(if (checked) R.string.blinky_tb_on else R.string.blinky_tb_off) } init { val inflater = LayoutInflater.from(context) - val view = inflater.inflate(R.layout.switch_control, null, false) - addView(view) + binding = SwitchControlBinding.inflate(inflater, null, false) + addView(binding.root) } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/activities/EnvironmentActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/activities/EnvironmentActivity.kt index 83a4abc7..2a03ee5f 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/activities/EnvironmentActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/activities/EnvironmentActivity.kt @@ -16,25 +16,24 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.lifecycle.Lifecycle -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.bluetooth.ble.GattCharacteristic import com.siliconlabs.bledemo.bluetooth.ble.GattService import com.siliconlabs.bledemo.bluetooth.ble.TimeoutGattCallback -import com.siliconlabs.bledemo.R -import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.viewmodels.EnvironmentViewModel -import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.dialogs.SettingsDialog -import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.control.* -import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.model.HallState -import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.model.TemperatureScale -import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.utils.SensorChecker.ThunderboardSensor +import com.siliconlabs.bledemo.databinding.ActivityEnvironmentBinding import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.activities.ThunderboardActivity import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.models.ThunderBoardDevice import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.utils.SensorChecker -import com.siliconlabs.bledemo.features.scan.browser.activities.DeviceServicesActivity +import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.utils.SensorChecker.ThunderboardSensor +import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.control.* +import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.dialogs.SettingsDialog +import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.model.HallState +import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.model.TemperatureScale +import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.viewmodels.EnvironmentViewModel import com.siliconlabs.bledemo.utils.BLEUtils import com.siliconlabs.bledemo.utils.Converters -import kotlinx.android.synthetic.main.activity_environment.* import timber.log.Timber class EnvironmentActivity : ThunderboardActivity() { @@ -43,14 +42,18 @@ class EnvironmentActivity : ThunderboardActivity() { private var hallStateControl: HallStateControl? = null private lateinit var viewModel: EnvironmentViewModel + private lateinit var binding: ActivityEnvironmentBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val view = LayoutInflater - .from(this).inflate(R.layout.activity_environment, null, false) - mainSection?.addView(view) - viewModel = ViewModelProvider(this, - EnvironmentViewModel.Factory(this)).get(EnvironmentViewModel::class.java) + binding = ActivityEnvironmentBinding.inflate( + layoutInflater + ) + mainSection?.addView(binding.root) + viewModel = ViewModelProvider( + this, + EnvironmentViewModel.Factory(this) + ).get(EnvironmentViewModel::class.java) setupDataListeners() } @@ -104,7 +107,8 @@ class EnvironmentActivity : ThunderboardActivity() { controls[ThunderboardSensor.MagneticField]?.setHallStrength(it) }) viewModel.hallState.observe(this, Observer { - hallStateControl?.setHallState(it) + // hallStateControl?.setHallState(it) + controls[ThunderboardSensor.DoorState]?.setHallState(it) }) } @@ -113,10 +117,14 @@ class EnvironmentActivity : ThunderboardActivity() { it.value == SensorChecker.SensorState.WORKING }.any() val dialogMessage = - if (isAnySensorWorking) getString(R.string.sensor_malfunction_dialog_message, - TextUtils.join(", ", brokenSensors)) - else getString(R.string.critical_sensor_malfunction_dialog_message, - TextUtils.join(", ", brokenSensors)) + if (isAnySensorWorking) getString( + R.string.sensor_malfunction_dialog_message, + TextUtils.join(", ", brokenSensors) + ) + else getString( + R.string.critical_sensor_malfunction_dialog_message, + TextUtils.join(", ", brokenSensors) + ) AlertDialog.Builder(this).apply { setTitle(getString(R.string.sensor_malfunction_dialog_title)) @@ -154,10 +162,12 @@ class EnvironmentActivity : ThunderboardActivity() { gatt?.disconnect() true } + R.id.action_settings -> { showSettings() true } + else -> super.onOptionsItemSelected(item) } } @@ -187,7 +197,7 @@ class EnvironmentActivity : ThunderboardActivity() { } private fun initGrid() { - env_grid.apply { + binding.envGrid.apply { sensorChecker.environmentSensors.filter { it.value == SensorChecker.SensorState.WORKING }.forEach { @@ -195,45 +205,70 @@ class EnvironmentActivity : ThunderboardActivity() { if (it.key == ThunderboardSensor.TVOC && !isPowerSufficient()) return@forEach if (it.key != ThunderboardSensor.DoorState) { - controls[it.key] = EnvironmentControl(this@EnvironmentActivity, - getString(getTileDescription(it.key)), - ContextCompat.getDrawable(this@EnvironmentActivity, getTileIcon(it.key))).also { - runOnUiThread { addView(it) } + controls[it.key] = EnvironmentControl( + this@EnvironmentActivity, + getString(getTileDescription(it.key)), + ContextCompat.getDrawable(this@EnvironmentActivity, getTileIcon(it.key)) + ).also { + runOnUiThread { addView(it.tileView.root) } } } else { - hallStateControl = HallStateControl(this@EnvironmentActivity, - getString(getTileDescription(it.key)), - ContextCompat.getDrawable(this@EnvironmentActivity, getTileIcon(it.key))).also { - it.setOnClickListener { onHallStateClick() } - runOnUiThread { addView(it) } + + controls[it.key] = EnvironmentControl( + this@EnvironmentActivity, + getString(getTileDescription(it.key)), + ContextCompat.getDrawable(this@EnvironmentActivity, getTileIcon(it.key)) + ).also { + runOnUiThread { addView(it.tileView.root) } } } } } + } private fun setupSensorCharacteristics() { sensorChecker.let { - it.setupEnvSensorCharacteristic(ThunderboardSensor.Temperature, - getEnvironmentalSensingCharacteristic(GattCharacteristic.EnvironmentTemperature)) - it.setupEnvSensorCharacteristic(ThunderboardSensor.Humidity, - getEnvironmentalSensingCharacteristic(GattCharacteristic.Humidity)) - it.setupEnvSensorCharacteristic(ThunderboardSensor.UvIndex, - getEnvironmentalSensingCharacteristic(GattCharacteristic.UvIndex)) - it.setupEnvSensorCharacteristic(ThunderboardSensor.Pressure, - getEnvironmentalSensingCharacteristic(GattCharacteristic.Pressure)) - it.setupEnvSensorCharacteristic(ThunderboardSensor.SoundLevel, - getEnvironmentalSensingCharacteristic(GattCharacteristic.SoundLevel)) - it.setupEnvSensorCharacteristic(ThunderboardSensor.AmbientLight, - getAmbientLightCharacteristic()) - it.setupEnvSensorCharacteristic(ThunderboardSensor.CO2, - getAirQualityCharacteristic(GattCharacteristic.CO2Reading)) - it.setupEnvSensorCharacteristic(ThunderboardSensor.TVOC, - getAirQualityCharacteristic(GattCharacteristic.TVOCReading)) - it.setupEnvSensorCharacteristic(ThunderboardSensor.MagneticField, - getHallEffectCharacteristic(GattCharacteristic.HallFieldStrength)) - it.setupEnvSensorCharacteristic(ThunderboardSensor.DoorState, - getHallEffectCharacteristic(GattCharacteristic.HallState)) + it.setupEnvSensorCharacteristic( + ThunderboardSensor.Temperature, + getEnvironmentalSensingCharacteristic(GattCharacteristic.EnvironmentTemperature) + ) + it.setupEnvSensorCharacteristic( + ThunderboardSensor.Humidity, + getEnvironmentalSensingCharacteristic(GattCharacteristic.Humidity) + ) + it.setupEnvSensorCharacteristic( + ThunderboardSensor.UvIndex, + getEnvironmentalSensingCharacteristic(GattCharacteristic.UvIndex) + ) + it.setupEnvSensorCharacteristic( + ThunderboardSensor.Pressure, + getEnvironmentalSensingCharacteristic(GattCharacteristic.Pressure) + ) + it.setupEnvSensorCharacteristic( + ThunderboardSensor.SoundLevel, + getEnvironmentalSensingCharacteristic(GattCharacteristic.SoundLevel) + ) + it.setupEnvSensorCharacteristic( + ThunderboardSensor.AmbientLight, + getAmbientLightCharacteristic() + ) + it.setupEnvSensorCharacteristic( + ThunderboardSensor.CO2, + getAirQualityCharacteristic(GattCharacteristic.CO2Reading) + ) + it.setupEnvSensorCharacteristic( + ThunderboardSensor.TVOC, + getAirQualityCharacteristic(GattCharacteristic.TVOCReading) + ) + it.setupEnvSensorCharacteristic( + ThunderboardSensor.MagneticField, + getHallEffectCharacteristic(GattCharacteristic.HallFieldStrength) + ) + it.setupEnvSensorCharacteristic( + ThunderboardSensor.DoorState, + getHallEffectCharacteristic(GattCharacteristic.HallState) + ) } } @@ -253,26 +288,28 @@ class EnvironmentActivity : ThunderboardActivity() { } private fun getEnvironmentalSensingCharacteristic(characteristic: GattCharacteristic): BluetoothGattCharacteristic? { - return gatt?.getService(GattService.EnvironmentalSensing.number)?. - getCharacteristic(characteristic.uuid) + return gatt?.getService(GattService.EnvironmentalSensing.number) + ?.getCharacteristic(characteristic.uuid) } private fun getAirQualityCharacteristic(characteristic: GattCharacteristic): BluetoothGattCharacteristic? { - return gatt?.getService(GattService.IndoorAirQuality.number)?. - getCharacteristic(characteristic.uuid) + return gatt?.getService(GattService.IndoorAirQuality.number) + ?.getCharacteristic(characteristic.uuid) } private fun getHallEffectCharacteristic(characteristic: GattCharacteristic): BluetoothGattCharacteristic? { - return gatt?.getService(GattService.HallEffect.number)?. - getCharacteristic(characteristic.uuid) + return gatt?.getService(GattService.HallEffect.number) + ?.getCharacteristic(characteristic.uuid) } - private fun getAmbientLightCharacteristic() : BluetoothGattCharacteristic? { + private fun getAmbientLightCharacteristic(): BluetoothGattCharacteristic? { val lightReact = getEnvironmentalSensingCharacteristic(GattCharacteristic.AmbientLightReact) if (lightReact != null) return lightReact - val lightReact2 = BLEUtils.getCharacteristic(gatt, GattService.AmbientLight, - GattCharacteristic.AmbientLightReact) + val lightReact2 = BLEUtils.getCharacteristic( + gatt, GattService.AmbientLight, + GattCharacteristic.AmbientLightReact + ) if (lightReact2 != null) return lightReact2 val lightSense = getEnvironmentalSensingCharacteristic(GattCharacteristic.AmbientLightSense) @@ -282,7 +319,7 @@ class EnvironmentActivity : ThunderboardActivity() { } @StringRes - private fun getTileDescription(sensor: ThunderboardSensor) : Int { + private fun getTileDescription(sensor: ThunderboardSensor): Int { return when (sensor) { ThunderboardSensor.Temperature -> R.string.environment_temp ThunderboardSensor.Humidity -> R.string.environment_humidity @@ -299,7 +336,7 @@ class EnvironmentActivity : ThunderboardActivity() { } @DrawableRes - private fun getTileIcon(sensor: ThunderboardSensor) : Int { + private fun getTileIcon(sensor: ThunderboardSensor): Int { return when (sensor) { ThunderboardSensor.Temperature -> R.drawable.icon_temp ThunderboardSensor.Humidity -> R.drawable.icon_environment @@ -333,9 +370,11 @@ class EnvironmentActivity : ThunderboardActivity() { queueReadingEnvironmentalData() } - override fun onCharacteristicRead(gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int) { + override fun onCharacteristicRead( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { super.onCharacteristicRead(gatt, characteristic, status) gattQueue.handleCommandProcessed() if (status != BluetoothGatt.GATT_SUCCESS) return @@ -347,36 +386,54 @@ class EnvironmentActivity : ThunderboardActivity() { GattCharacteristic.ModelNumberString, GattCharacteristic.BatteryLevel, GattCharacteristic.PowerSource, - GattCharacteristic.FirmwareRevision -> statusFragment.handleBaseCharacteristic(characteristic) + GattCharacteristic.FirmwareRevision -> statusFragment.handleBaseCharacteristic( + characteristic + ) GattCharacteristic.EnvironmentTemperature -> { val temperature = characteristic.getIntValue(gattCharacteristic.format, 0) if (setup) { - sensorChecker.checkIfEnvSensorBroken(ThunderboardSensor.Temperature, temperature.toLong()) + sensorChecker.checkIfEnvSensorBroken( + ThunderboardSensor.Temperature, + temperature.toLong() + ) } else viewModel.incrementControlsRead() viewModel.temperature.postValue(temperature / 100.0f) } + GattCharacteristic.Humidity -> { val humidity = characteristic.getIntValue(gattCharacteristic.format, 0) if (setup) { - sensorChecker.checkIfEnvSensorBroken(ThunderboardSensor.Humidity, humidity.toLong()) + sensorChecker.checkIfEnvSensorBroken( + ThunderboardSensor.Humidity, + humidity.toLong() + ) } else viewModel.incrementControlsRead() viewModel.humidity.postValue(humidity / 100) } + GattCharacteristic.UvIndex -> { val uvIndex = characteristic.getIntValue(gattCharacteristic.format, 0) if (setup) { - sensorChecker.checkIfEnvSensorBroken(ThunderboardSensor.UvIndex, uvIndex.toLong()) + sensorChecker.checkIfEnvSensorBroken( + ThunderboardSensor.UvIndex, + uvIndex.toLong() + ) } else viewModel.incrementControlsRead() viewModel.uvIndex.postValue(uvIndex) } + GattCharacteristic.SoundLevel -> { val soundLevel = characteristic.getIntValue(gattCharacteristic.format, 0) if (setup) { - sensorChecker.checkIfEnvSensorBroken(ThunderboardSensor.SoundLevel, soundLevel.toLong()) + sensorChecker.checkIfEnvSensorBroken( + ThunderboardSensor.SoundLevel, + soundLevel.toLong() + ) } else viewModel.incrementControlsRead() viewModel.soundLevel.postValue(soundLevel / 100) } + GattCharacteristic.Pressure -> { val pressure = Converters.calculateLongValue(characteristic.value, false) if (setup) { @@ -384,24 +441,36 @@ class EnvironmentActivity : ThunderboardActivity() { } else viewModel.incrementControlsRead() viewModel.pressure.postValue(pressure / 1000) } + GattCharacteristic.CO2Reading -> { val co2Level = characteristic.getIntValue(gattCharacteristic.format, 0) if (setup) { - sensorChecker.checkIfEnvSensorBroken(ThunderboardSensor.CO2, co2Level.toLong()) + sensorChecker.checkIfEnvSensorBroken( + ThunderboardSensor.CO2, + co2Level.toLong() + ) } else viewModel.incrementControlsRead() viewModel.co2Level.postValue(co2Level) } + GattCharacteristic.TVOCReading -> { val tvocLevel = characteristic.getIntValue(gattCharacteristic.format, 0) if (setup) { - sensorChecker.checkIfEnvSensorBroken(ThunderboardSensor.TVOC, tvocLevel.toLong()) + sensorChecker.checkIfEnvSensorBroken( + ThunderboardSensor.TVOC, + tvocLevel.toLong() + ) } else viewModel.incrementControlsRead() viewModel.tvocLevel.postValue(tvocLevel) } + GattCharacteristic.HallFieldStrength -> { val hallStrength = characteristic.getIntValue(gattCharacteristic.format, 0) if (setup) { - sensorChecker.checkIfEnvSensorBroken(ThunderboardSensor.MagneticField, hallStrength.toLong()) + sensorChecker.checkIfEnvSensorBroken( + ThunderboardSensor.MagneticField, + hallStrength.toLong() + ) val brokenSensors = sensorChecker.environmentSensors.filter { it.value == SensorChecker.SensorState.BROKEN }.keys @@ -417,6 +486,7 @@ class EnvironmentActivity : ThunderboardActivity() { viewModel.hallStrength.postValue(hallStrength) } + GattCharacteristic.HallState -> { val hallState = characteristic.getIntValue(gattCharacteristic.format, 0) viewModel.hallState.postValue(HallState.fromValue(hallState)) @@ -433,21 +503,27 @@ class EnvironmentActivity : ThunderboardActivity() { GattCharacteristic.AmbientLightSense -> { var ambientLight = Converters.calculateLongValue(characteristic.value, false) if (setup) { - sensorChecker.checkIfEnvSensorBroken(ThunderboardSensor.AmbientLight, ambientLight) + sensorChecker.checkIfEnvSensorBroken( + ThunderboardSensor.AmbientLight, + ambientLight + ) } else viewModel.incrementControlsRead() ambientLight /= 100 viewModel.ambientLight.postValue( - if (ambientLight > MAX_AMBIENT_LIGHT) MAX_AMBIENT_LIGHT.toLong() - else ambientLight + if (ambientLight > MAX_AMBIENT_LIGHT) MAX_AMBIENT_LIGHT.toLong() + else ambientLight ) } - else -> { } + + else -> {} } } - override fun onCharacteristicWrite(gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int) { + override fun onCharacteristicWrite( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { super.onCharacteristicWrite(gatt, characteristic, status) gattQueue.handleCommandProcessed() if (status != BluetoothGatt.GATT_SUCCESS) return @@ -457,25 +533,34 @@ class EnvironmentActivity : ThunderboardActivity() { } } - override fun onCharacteristicChanged(gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic) { + override fun onCharacteristicChanged( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic + ) { super.onCharacteristicChanged(gatt, characteristic) val gattCharacteristic = GattCharacteristic.fromUuid(characteristic.uuid) when (gattCharacteristic) { GattCharacteristic.BatteryLevel, - GattCharacteristic.PowerSource -> statusFragment.handleBaseCharacteristic(characteristic) + GattCharacteristic.PowerSource -> statusFragment.handleBaseCharacteristic( + characteristic + ) GattCharacteristic.HallState -> { val hallState = characteristic.getIntValue(gattCharacteristic.format, 0) viewModel.hallState.postValue(HallState.fromValue(hallState)) } - else -> { } + + else -> {} } } - override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { + override fun onDescriptorWrite( + gatt: BluetoothGatt, + descriptor: BluetoothGattDescriptor, + status: Int + ) { super.onDescriptorWrite(gatt, descriptor, status) gattQueue.handleCommandProcessed() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/control/EnvironmentControl.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/control/EnvironmentControl.kt index c29524e2..caa0e1ae 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/control/EnvironmentControl.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/control/EnvironmentControl.kt @@ -2,74 +2,123 @@ package com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environme import android.content.Context import android.graphics.drawable.Drawable +import android.view.LayoutInflater import android.view.View import android.widget.LinearLayout +import android.widget.TextView +import androidx.annotation.StringRes +import androidx.annotation.StyleRes import androidx.gridlayout.widget.GridLayout import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.EnvironmentdemoTileBinding +import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.model.HallState import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.model.TemperatureScale -import kotlinx.android.synthetic.main.environmentdemo_tile.view.* + +//import kotlinx.android.synthetic.main.environmentdemo_tile.view.* open class EnvironmentControl( - context: Context, - description: String?, - icon: Drawable? + context: Context, + description: String?, + icon: Drawable? ) : LinearLayout(context, null, 0) { - constructor(context: Context) : this(context, null, null) - private val tileView: View = inflate(context, R.layout.environmentdemo_tile, this) + constructor(context: Context) : this(context, null, null) + val tileView = EnvironmentdemoTileBinding.inflate(LayoutInflater.from(context)) + lateinit var resetTamperTextView: TextView fun setTemperature(temperature: Float, temperatureType: Int) { - tileView.env_value.text = String.format( - if (temperatureType == TemperatureScale.FAHRENHEIT) context.getString(R.string.environment_temp_f) else context.getString( - R.string.environment_temp_c), - if (temperatureType == TemperatureScale.FAHRENHEIT) temperature * 1.8f + 32f else temperature) + + tileView.envValue.text = String.format( + if (temperatureType == TemperatureScale.FAHRENHEIT) context.getString(R.string.environment_temp_f) else context.getString( + R.string.environment_temp_c + ), + if (temperatureType == TemperatureScale.FAHRENHEIT) temperature * 1.8f + 32f else temperature + ) } fun setHumidity(humidity: Int) { - tileView.env_value.text = String.format(context.getString(R.string.environment_humidity_measure), humidity) + tileView.envValue.text = + String.format(context.getString(R.string.environment_humidity_measure), humidity) } fun setUVIndex(uvIndex: Int) { - tileView.env_value.text = String.format(context.getString(R.string.environment_uv_unit), uvIndex) + tileView.envValue.text = + String.format(context.getString(R.string.environment_uv_unit), uvIndex) } fun setAmbientLight(ambientLight: Long) { - tileView.env_value.text = String.format(context.getString(R.string.environment_ambient_lx), ambientLight) + tileView.envValue.text = + String.format(context.getString(R.string.environment_ambient_lx), ambientLight) } fun setSoundLevel(soundLevel: Int) { - tileView.env_value.text = String.format(context.getString(R.string.environment_sound_level_measure), soundLevel) + tileView.envValue.text = + String.format(context.getString(R.string.environment_sound_level_measure), soundLevel) } fun setPressure(pressure: Long) { - tileView.env_value.text = String.format(context.getString(R.string.environment_pressure_measure), pressure) + tileView.envValue.text = + String.format(context.getString(R.string.environment_pressure_measure), pressure) } fun setCO2(co2Level: Int) { - tileView.env_value.text = String.format(context.getString(R.string.environment_co2_measure), co2Level) + tileView.envValue.text = + String.format(context.getString(R.string.environment_co2_measure), co2Level) } fun setVOC(vocLevel: Int) { - tileView.env_value.text = String.format(context.getString(R.string.environment_voc_measure), vocLevel) + tileView.envValue.text = + String.format(context.getString(R.string.environment_voc_measure), vocLevel) } fun setHallStrength(hallStrength: Int) { - tileView.env_value.text = context.getString(R.string.environment_hall_strength_measure, hallStrength) + tileView.envValue.text = + context.getString(R.string.environment_hall_strength_measure, hallStrength) } init { + if(description!!.contains(context.getString(R.string.environment_hall_state))){ + resetTamperTextView = TextView(context).apply { + setText(R.string.environment_hall_state_reset_tamper) + setTextAppearance(R.style.EnvironmentControlLabel_HallStateTampered) + visibility = GONE + } + tileView.envLayout.addView(resetTamperTextView) + tileView.cardviewEnvTile.isEnabled = false + } tileView.apply { - env_description.text = description - env_value.text = context.getString(R.string.environment_not_initialized) - env_icon.setImageDrawable(icon) + envDescription.text = description + envValue.text = context.getString(R.string.environment_not_initialized) + envIcon.setImageDrawable(icon) } layoutParams = GridLayout.LayoutParams( - GridLayout.spec(GridLayout.UNDEFINED, 1f), - GridLayout.spec(GridLayout.UNDEFINED, 1f)).apply { + GridLayout.spec(GridLayout.UNDEFINED, 1f), + GridLayout.spec(GridLayout.UNDEFINED, 1f) + ).apply { width = 0 } } + fun setHallState(hallState: HallState) { + var resetTamperVisible = GONE + @StringRes var hallStateTextResId = R.string.environment_not_initialized + @StyleRes var hallStateStyleResId = R.style.tb_robo_medium_18dp + + when (hallState) { + HallState.TAMPERED -> { + resetTamperVisible = VISIBLE + hallStateTextResId = R.string.environment_hall_state_tampered + hallStateStyleResId = R.style.EnvironmentControlLabel_HallStateTampered + } + + HallState.CLOSED -> hallStateTextResId = R.string.environment_hall_state_closed + HallState.OPENED -> hallStateTextResId = R.string.environment_hall_state_opened + } + + tileView.envValue.text = context.getString(hallStateTextResId) + tileView.envValue.setTextAppearance(hallStateStyleResId) + resetTamperTextView.visibility = resetTamperVisible + } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/control/HallStateControl.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/control/HallStateControl.kt index 5cfe6ebc..d3700bbd 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/control/HallStateControl.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/control/HallStateControl.kt @@ -2,13 +2,15 @@ package com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environme import android.content.Context import android.graphics.drawable.Drawable -import android.view.View +import android.view.LayoutInflater import android.widget.TextView import androidx.annotation.StringRes import androidx.annotation.StyleRes import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.EnvironmentdemoTileBinding import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.model.HallState -import kotlinx.android.synthetic.main.environmentdemo_tile.view.* + +//import kotlinx.android.synthetic.main.environmentdemo_tile.view.* /** * Displays an icon and the Hall State value in a combo control. @@ -17,14 +19,14 @@ import kotlinx.android.synthetic.main.environmentdemo_tile.view.* * The HallStateMeter and the TextView are added to the layout dynamically */ class HallStateControl( - context: Context, - description: String?, - icon: Drawable? + context: Context, + description: String?, + icon: Drawable? ) : EnvironmentControl(context, description, icon) { constructor(context: Context) : this(context, null, null) - private val tileView: View = inflate(context, R.layout.environmentdemo_tile, this) + /*val tileViewHallState = EnvironmentdemoTileBinding.inflate(LayoutInflater.from(context)) private val resetTamperTextView: TextView fun setHallState(hallState: HallState) { @@ -38,12 +40,13 @@ class HallStateControl( hallStateTextResId = R.string.environment_hall_state_tampered hallStateStyleResId = R.style.EnvironmentControlLabel_HallStateTampered } + HallState.CLOSED -> hallStateTextResId = R.string.environment_hall_state_closed HallState.OPENED -> hallStateTextResId = R.string.environment_hall_state_opened } - tileView.env_value.text = context.getString(hallStateTextResId) - tileView.env_value.setTextAppearance(hallStateStyleResId) + tileViewHallState.envValue.text = context.getString(hallStateTextResId) + tileViewHallState.envValue.setTextAppearance(hallStateStyleResId) resetTamperTextView.visibility = resetTamperVisible } @@ -53,7 +56,7 @@ class HallStateControl( setTextAppearance(R.style.EnvironmentControlLabel_HallStateTampered) visibility = GONE } - env_layout.addView(resetTamperTextView) - cardview_env_tile.isEnabled = false - } + tileViewHallState.envLayout.addView(resetTamperTextView) + tileViewHallState.cardviewEnvTile.isEnabled = false + }*/ } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/dialogs/SettingsDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/dialogs/SettingsDialog.kt index 37519c75..5e418d6e 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/dialogs/SettingsDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/environment/dialogs/SettingsDialog.kt @@ -11,22 +11,24 @@ import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.databinding.DialogSettingsBinding import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.model.TemperatureScale import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.environment.utils.PreferenceManager -import kotlinx.android.synthetic.main.dialog_settings.* + class SettingsDialog( - context: Context, - private val settingsHandler: SettingsHandler + context: Context, + private val settingsHandler: SettingsHandler ) : BaseDialogFragment( - hasCustomWidth = true, - isCanceledOnTouchOutside = true + hasCustomWidth = true, + isCanceledOnTouchOutside = true ) { private val prefsManager = PreferenceManager(context) private lateinit var _binding: DialogSettingsBinding - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { _binding = DialogSettingsBinding.inflate(inflater) return _binding.root } @@ -49,9 +51,9 @@ class SettingsDialog( private fun loadPersonalize() { prefsManager.preferences.let { if (it.scale == TemperatureScale.CELSIUS) { - temperature_toggle.check(R.id.celsius) + _binding.temperatureToggle.check(R.id.celsius) } else if (it.scale == TemperatureScale.FAHRENHEIT) { - temperature_toggle.check(R.id.fahrenheit) + _binding.temperatureToggle.check(R.id.fahrenheit) } } } @@ -59,8 +61,8 @@ class SettingsDialog( private fun saveSettings() { prefsManager.preferences.let { it.scale = - if (temperature_toggle.checkedRadioButtonId == R.id.celsius) TemperatureScale.CELSIUS - else TemperatureScale.FAHRENHEIT + if (_binding.temperatureToggle.checkedRadioButtonId == R.id.celsius) TemperatureScale.CELSIUS + else TemperatureScale.FAHRENHEIT prefsManager.savePreferences(it) } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/motion/activities/MotionActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/motion/activities/MotionActivity.kt index d48a0ed8..9dff7433 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/motion/activities/MotionActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/motion/activities/MotionActivity.kt @@ -19,14 +19,12 @@ import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.bluetooth.ble.GattCharacteristic import com.siliconlabs.bledemo.bluetooth.ble.GattService import com.siliconlabs.bledemo.bluetooth.ble.TimeoutGattCallback +import com.siliconlabs.bledemo.databinding.ActivityMotionBinding import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.utils.SensorChecker import com.siliconlabs.bledemo.features.demo.thunderboard_demos.base.utils.SensorChecker.ThunderboardSensor import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.motion.adapters.GdxAdapter import com.siliconlabs.bledemo.features.demo.thunderboard_demos.demos.motion.viewmodels.MotionViewModel import com.siliconlabs.bledemo.home_screen.dialogs.SelectDeviceDialog -import kotlinx.android.synthetic.main.activity_motion.* -import kotlinx.android.synthetic.main.motiondemo_acceleration.* -import kotlinx.android.synthetic.main.motiondemo_orientation.* import timber.log.Timber class MotionActivity : GdxActivity() { @@ -35,12 +33,13 @@ class MotionActivity : GdxActivity() { private var gdxAdapter: GdxAdapter? = null private lateinit var viewModel: MotionViewModel + private lateinit var binding: ActivityMotionBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val view = LayoutInflater.from(this) - .inflate(R.layout.activity_motion, null, false) - mainSection?.addView(view) + binding = ActivityMotionBinding + .inflate(LayoutInflater.from(this), null, false) + mainSection?.addView(binding.root) viewModel = ViewModelProvider(this).get(MotionViewModel::class.java) setupClickListeners() @@ -82,22 +81,23 @@ class MotionActivity : GdxActivity() { private fun setAcceleration(x: Float, y: Float, z: Float) { val accelerationString = getString(R.string.motion_acceleration_g) - acceleration_x.text = String.format(accelerationString, x) - acceleration_y.text = String.format(accelerationString, y) - acceleration_z.text = String.format(accelerationString, z) + binding.motionDemoAccelerationParent.accelerationX.text = String.format(accelerationString, x) + binding.motionDemoAccelerationParent.accelerationY.text = String.format(accelerationString, y) + binding.motionDemoAccelerationParent.accelerationZ.text = String.format(accelerationString, z) } // Angles are measured in degrees (-180 to 180) private fun setOrientation(x: Float, y: Float, z: Float) { val degreeString = getString(R.string.motion_orientation_degree) - orientation_x.text = String.format(degreeString, x) - orientation_y.text = String.format(degreeString, y) - orientation_z.text = String.format(degreeString, z) + + binding.motionDemoOrientationParent.orientationX.text = String.format(degreeString, x) + binding.motionDemoOrientationParent.orientationY.text = String.format(degreeString, y) + binding.motionDemoOrientationParent.orientationZ.text = String.format(degreeString, z) gdxAdapter?.setOrientation(x, y, z) } private fun setupClickListeners() { - calibrate.setOnClickListener { + binding.calibrate .setOnClickListener { popupCalibratingDialog() Handler(Looper.getMainLooper()).postDelayed({ viewModel.calibrate(gatt) @@ -134,8 +134,8 @@ class MotionActivity : GdxActivity() { } private fun setCalibrateVisible(enabled: Boolean) { - calibrate.visibility = - if (enabled) View.VISIBLE + binding.calibrate.visibility = + if (enabled) View.VISIBLE else View.INVISIBLE } @@ -156,15 +156,19 @@ class MotionActivity : GdxActivity() { useWakelock = false } val gdx3dView = initializeForView(gdxAdapter, config) - car_animation.addView(gdx3dView) + binding.carAnimation .addView(gdx3dView) } @SuppressLint("MissingPermission") private fun showBrokenSensorsMessage(brokenSensors: Set) { AlertDialog.Builder(this).apply { setTitle(getString(R.string.sensor_malfunction_dialog_title)) - setMessage(getString(R.string.critical_sensor_malfunction_dialog_message, - TextUtils.join(", ", brokenSensors))) + setMessage( + getString( + R.string.critical_sensor_malfunction_dialog_message, + TextUtils.join(", ", brokenSensors) + ) + ) setPositiveButton(getString(R.string.button_ok)) { _, _ -> gatt?.disconnect() ?: onDeviceDisconnected() } @@ -201,9 +205,11 @@ class MotionActivity : GdxActivity() { queueMotionNotificationsSetup() } - override fun onCharacteristicRead(gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int) { + override fun onCharacteristicRead( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { super.onCharacteristicRead(gatt, characteristic, status) gattQueue.handleCommandProcessed() if (status != BluetoothGatt.GATT_SUCCESS) return @@ -215,14 +221,19 @@ class MotionActivity : GdxActivity() { GattCharacteristic.ModelNumberString, GattCharacteristic.BatteryLevel, GattCharacteristic.PowerSource, - GattCharacteristic.FirmwareRevision -> statusFragment.handleBaseCharacteristic(characteristic) - else -> { } + GattCharacteristic.FirmwareRevision -> statusFragment.handleBaseCharacteristic( + characteristic + ) + + else -> {} } } - override fun onCharacteristicWrite(gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int) { + override fun onCharacteristicWrite( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { super.onCharacteristicWrite(gatt, characteristic, status) if (status != BluetoothGatt.GATT_SUCCESS) return @@ -230,20 +241,24 @@ class MotionActivity : GdxActivity() { when (characteristic.value[0]) { 0x01.toByte() -> viewModel.resetOrientation(gatt, characteristic) 0x02.toByte() -> closeCalibratingDialog() - else -> { } + else -> {} } } } - override fun onCharacteristicChanged(gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic) { + override fun onCharacteristicChanged( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic + ) { super.onCharacteristicChanged(gatt, characteristic) val gattCharacteristic = GattCharacteristic.fromUuid(characteristic.uuid) when (gattCharacteristic) { GattCharacteristic.BatteryLevel, - GattCharacteristic.PowerSource -> statusFragment.handleBaseCharacteristic(characteristic) + GattCharacteristic.PowerSource -> statusFragment.handleBaseCharacteristic( + characteristic + ) GattCharacteristic.Acceleration -> { val accelerationX = characteristic.getIntValue(gattCharacteristic.format, 0) @@ -252,13 +267,19 @@ class MotionActivity : GdxActivity() { Timber.d("Acceleration; X = $accelerationX, Y = $accelerationY, Z = $accelerationZ") if (setup) { - sensorChecker.checkIfMotionSensorBroken(ThunderboardSensor.Acceleration, - accelerationX, accelerationY, accelerationZ) + sensorChecker.checkIfMotionSensorBroken( + ThunderboardSensor.Acceleration, + accelerationX, accelerationY, accelerationZ + ) } - viewModel.acceleration.postValue(floatArrayOf( - accelerationX / 1000f, accelerationY / 1000f, accelerationZ / 1000f)) + viewModel.acceleration.postValue( + floatArrayOf( + accelerationX / 1000f, accelerationY / 1000f, accelerationZ / 1000f + ) + ) } + GattCharacteristic.Orientation -> { val orientationX = characteristic.getIntValue(gattCharacteristic.format, 0) val orientationY = characteristic.getIntValue(gattCharacteristic.format, 2) @@ -266,8 +287,10 @@ class MotionActivity : GdxActivity() { Timber.d("Orientation; X = $orientationX, Y = $orientationY, Z = $orientationZ") if (setup) { - sensorChecker.checkIfMotionSensorBroken(ThunderboardSensor.Orientation, - orientationX, orientationY, orientationZ) + sensorChecker.checkIfMotionSensorBroken( + ThunderboardSensor.Orientation, + orientationX, orientationY, orientationZ + ) val brokenSensors = sensorChecker.motionSensors.filter { it.value == SensorChecker.SensorState.BROKEN }.keys @@ -280,21 +303,31 @@ class MotionActivity : GdxActivity() { } } - viewModel.orientation.postValue(floatArrayOf( - orientationX / 100f, orientationY / 100f, orientationZ / 100f)) + viewModel.orientation.postValue( + floatArrayOf( + orientationX / 100f, orientationY / 100f, orientationZ / 100f + ) + ) } + GattCharacteristic.Calibration -> { when (characteristic.value[1]) { - 0x01.toByte() -> viewModel.resetOrientation(gatt, characteristic) /* Somehow needed for properly resetting char's value */ - else -> { } + 0x01.toByte() -> viewModel.resetOrientation( + gatt, + characteristic + ) /* Somehow needed for properly resetting char's value */ + else -> {} } } - else -> { } + + else -> {} } } - override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, - status: Int) { + override fun onDescriptorWrite( + gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, + status: Int + ) { super.onDescriptorWrite(gatt, descriptor, status) gattQueue.handleCommandProcessed() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_commissioning/activities/WifiCommissioningActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_commissioning/activities/WifiCommissioningActivity.kt index 0dafd412..f6d21143 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_commissioning/activities/WifiCommissioningActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_commissioning/activities/WifiCommissioningActivity.kt @@ -200,6 +200,8 @@ class WifiCommissioningActivity : BaseDemoActivity() { } BluetoothService.GattConnectType.DEV_KIT_SENSOR -> { + // println("--------------Connected${connectedAccessPoint!!.ipAddress}") + // println("--------------Connected${clickedAccessPoint!!.ipAddress}") val devKitIntent = Intent( this, DevKitSensor917Activity::class.java diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_commissioning/adapters/AccessPointsAdapter.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_commissioning/adapters/AccessPointsAdapter.kt index 23ca6088..8147a0c1 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_commissioning/adapters/AccessPointsAdapter.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_commissioning/adapters/AccessPointsAdapter.kt @@ -17,13 +17,13 @@ import java.util.* * Created by harika on 18-04-2016. */ class AccessPointsAdapter( - private val accessPoints: ArrayList, - private val listener: OnItemClickListener + private val accessPoints: ArrayList, + private val listener: OnItemClickListener ) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccessPointViewHolder { val itemView = LayoutInflater.from(parent.context) - .inflate(R.layout.adapter_access_point, parent, false) + .inflate(R.layout.adapter_access_point, parent, false) return AccessPointViewHolder(itemView) } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/AlertErrorDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/AlertErrorDialog.kt index 49e69cb6..96b7b4fc 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/AlertErrorDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/AlertErrorDialog.kt @@ -7,23 +7,29 @@ import android.view.View import android.view.ViewGroup import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.dialog_error.* +import com.siliconlabs.bledemo.databinding.DialogAlertErrorBinding + class AlertErrorDialog( private val otaErrorCallback: OtaErrorCallback ) : BaseDialogFragment( - hasCustomWidth = true, - isCanceledOnTouchOutside = false + hasCustomWidth = true, + isCanceledOnTouchOutside = false ) { + private lateinit var binding: DialogAlertErrorBinding - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_alert_error, container, false) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogAlertErrorBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - btn_ok.setOnClickListener { + binding.btnOk.setOnClickListener { dismiss() otaErrorCallback.onDismiss() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileSelectionDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileSelectionDialog.kt index 648b5f07..0f4558e3 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileSelectionDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileSelectionDialog.kt @@ -7,18 +7,22 @@ import android.view.View import android.view.ViewGroup import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.databinding.DialogWifiOtaFileUpdateBinding -import kotlinx.android.synthetic.main.dialog_ota_file_selection_iop.ota_cancel -class WiFiOtaFileSelectionDialog(private val cancelCallback: CancelCallback, - private val listener: FileSelectionListener, private val ipAddress: String?) : BaseDialogFragment( - hasCustomWidth = true, - isCanceledOnTouchOutside = false + +class WiFiOtaFileSelectionDialog( + private val cancelCallback: CancelCallback, + private val listener: FileSelectionListener, private val ipAddress: String? +) : BaseDialogFragment( + hasCustomWidth = true, + isCanceledOnTouchOutside = false ) { private lateinit var _binding: DialogWifiOtaFileUpdateBinding - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { _binding = DialogWifiOtaFileUpdateBinding.inflate(inflater) _binding.wifiIpAddress.text = ipAddress return _binding.root @@ -27,7 +31,7 @@ class WiFiOtaFileSelectionDialog(private val cancelCallback: CancelCallback, override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setupUiListeners() - ota_cancel.setOnClickListener { + _binding.otaCancel.setOnClickListener { dismiss() cancelCallback.onDismiss() } @@ -49,6 +53,7 @@ class WiFiOtaFileSelectionDialog(private val cancelCallback: CancelCallback, fun checkPortNumberValid(): Boolean { return _binding.portId.text.length >= 4 } + fun changeFileName(newName: String?) { _binding.selectAppFileBtn.text = newName } @@ -61,7 +66,7 @@ class WiFiOtaFileSelectionDialog(private val cancelCallback: CancelCallback, _binding.otaProceed.isEnabled = false } - fun getPortId() : String { + fun getPortId(): String { return _binding.portId.text.toString() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/activities/WifiThroughputActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/activities/WifiThroughputActivity.kt new file mode 100644 index 00000000..383f14d7 --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/activities/WifiThroughputActivity.kt @@ -0,0 +1,436 @@ +package com.siliconlabs.bledemo.features.demo.wifi_throughput.activities + +import android.content.Context +import android.net.InetAddresses +import android.net.wifi.WifiManager +import android.os.Build +import android.os.Bundle +import android.util.Patterns +import android.view.MenuItem +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Card +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.material.TextField +import androidx.compose.material.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +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.platform.ComposeView +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import androidx.fragment.app.Fragment +import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.ActivityWifiThroughputBinding +import com.siliconlabs.bledemo.features.demo.wifi_throughput.fragments.WifiThroughPutDetailScreen +import com.siliconlabs.bledemo.features.demo.wifi_throughput.utils.ThroughputUtils +import com.siliconlabs.bledemo.features.iop_test.utils.Utils +import java.net.InetAddress +import java.nio.ByteBuffer +import java.nio.ByteOrder + +class WifiThroughputActivity : AppCompatActivity() { + + private lateinit var binding: ActivityWifiThroughputBinding + private val throughPutDemos = ThroughputUtils.WiFiThroughPutFeature.values() + private lateinit var context: Context + private var isConfirmCalled = mutableStateOf(false) + var ipAddress by mutableStateOf("") + var portNumber by mutableStateOf("") + var userSelectedFeature: Int by mutableIntStateOf(0) + private var isDownload = mutableStateOf(false) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + context = this@WifiThroughputActivity + binding = ActivityWifiThroughputBinding.inflate(layoutInflater) + setContentView(binding.root) + + findViewById(R.id.my_composable).setContent { + GridLayout(this, throughPutDemos) + val isConfirm = remember { isConfirmCalled } + if (isConfirm.value) { + //FragmentContainer() + } + } + + //setSupportActionBar(binding.toolbar) + val actionBar = supportActionBar + actionBar!!.setHomeAsUpIndicator(R.drawable.matter_back) + actionBar.setDisplayHomeAsUpEnabled(true) + actionBar.setTitle(R.string.wifi_title_Throughput) + } + + // Function to update the ActionBar title + fun updateActionBarTitle(title: String) { + supportActionBar?.title = title + } + + // Function to reset the title back to the static one + fun resetActionBarTitle() { + supportActionBar?.title = getString(R.string.wifi_title_Throughput) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + android.R.id.home -> { + onBackPressed() + true + } + + else -> super.onOptionsItemSelected(item) + } + } + + override fun onBackPressed() { + resetActionBarTitle() + val myFragment: WifiThroughPutDetailScreen? = + supportFragmentManager.findFragmentByTag("tag") as WifiThroughPutDetailScreen? + if (myFragment != null && myFragment.isVisible()) { + supportFragmentManager.popBackStack() + } + super.onBackPressed() + } + + private fun showThroughputDetailScreen( + userSelectedOption: Int, + ipAddress: String, + portNumber: String + ) { + val mBundle = Bundle() + mBundle.putString( + ThroughputUtils.throughPutType, + ThroughputUtils.getTitle(userSelectedOption, context) + ) + mBundle.putString(ThroughputUtils.ipAddress, ipAddress) + mBundle.putString(ThroughputUtils.portNumber, portNumber) + val fragment = WifiThroughPutDetailScreen() + showFragment( + fragment, + mBundle, + fragment::class.java.simpleName + ) + } + + private fun showFragment( + fragment: Fragment, bundle: Bundle, tag: String? = null, + ) { + val fManager = supportFragmentManager + val fTransaction = fManager.beginTransaction() + fragment.setArguments(bundle) + + fTransaction.add(binding.throughputContainer, fragment, tag) + .addToBackStack(null) + .commit() + + } + + @OptIn(ExperimentalMaterialApi::class) + @Composable + fun GridLayout( + context: Context, + throughPutDemos: Array + ) { + val dialogState: MutableState = remember { + mutableStateOf(false) + } + val dialogHeaderTitle: MutableState = remember { mutableStateOf("") } + val dialogHeaderSubTitle: MutableState = remember { mutableStateOf("") } + LazyVerticalGrid(columns = GridCells.Fixed(2), + contentPadding = PaddingValues( + start = 12.dp, + top = 16.dp, + end = 12.dp, + bottom = 16.dp + ), + content = { + items(throughPutDemos.size) { + Card( + backgroundColor = colorResource(R.color.silabs_white), + shape = RoundedCornerShape(8.dp), + modifier = Modifier + .padding(4.dp) + .size(150.dp) + .fillMaxWidth(), + elevation = 8.dp, + onClick = { + dialogState.value = true + if (ThroughputUtils.getTitle( + it, + context + ) == ThroughputUtils.THROUGHPUT_TYPE_TCP_DOWNLOAD + ) { + dialogHeaderTitle.value = getString(R.string.tcp_server) + dialogHeaderSubTitle.value = + getString(R.string.tcp_download_sub_title) + } else if (ThroughputUtils.getTitle( + it, + context + ) == ThroughputUtils.THROUGHPUT_TYPE_TCP_UPLOAD + ) { + dialogHeaderTitle.value = getString(R.string.tcp_client) + dialogHeaderSubTitle.value = + getString(R.string.tcp_upload_sub_title) + } else if (ThroughputUtils.getTitle( + it, + context + ) == ThroughputUtils.THROUGHPUT_TYPE_UDP_DOWNLOAD + ) { + dialogHeaderTitle.value = getString(R.string.udp_server) + dialogHeaderSubTitle.value = + getString(R.string.dialog_udp_download_sub_title) + } else if (ThroughputUtils.getTitle( + it, + context + ) == ThroughputUtils.THROUGHPUT_TYPE_UDP_UPLOAD + ) { + dialogHeaderTitle.value = getString(R.string.udp_client) + dialogHeaderSubTitle.value = + getString(R.string.dialog_udp_upload_sub_title) + } else if (ThroughputUtils.getTitle( + it, + context + ) == ThroughputUtils.THROUGHPUT_TYPE_TLS_DOWNLOAD + ) { + dialogHeaderTitle.value = getString(R.string.tls_server) + dialogHeaderSubTitle.value = + getString(R.string.dialog_tls_download_sub_title) + } else if (ThroughputUtils.getTitle( + it, + context + ) == ThroughputUtils.THROUGHPUT_TYPE_TLS_UPLOAD + ) { + dialogHeaderTitle.value = getString(R.string.tls_server) + dialogHeaderSubTitle.value = + getString(R.string.dialog_sub_title_tls_upload) + } + userSelectedFeature = it + if (ThroughputUtils.isThroughPutTypeDownload(it)) { + isDownload.value = false + ipAddress = getLocalIpAddress(context) + } else { + isDownload.value = true + ipAddress = "" + } + } + ) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.fillMaxSize() + ) { + Text( + text = ThroughputUtils.getTitle(it, context), + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + fontFamily = FontFamily.SansSerif, + color = colorResource(id = R.color.silabs_black), + textAlign = TextAlign.Center, + modifier = Modifier.padding(16.dp) + ) + } + + } + } + }) + + if (dialogState.value) { + Dialog( + onDismissRequest = ({ + dialogState.value = false + isConfirmCalled.value = false + }), + properties = DialogProperties( + dismissOnBackPress = true, + dismissOnClickOutside = false + ) + ) { + Card( + modifier = Modifier + .fillMaxWidth() + .height(450.dp) + .padding(16.dp), + shape = RoundedCornerShape(16.dp), + ) { + Column( + modifier = Modifier + .fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + text = dialogHeaderTitle.value, + fontWeight = FontWeight.Black, + fontSize = 22.sp, + fontFamily = FontFamily.SansSerif, + color = colorResource(id = R.color.silabs_black), + textAlign = TextAlign.Center, + modifier = Modifier.padding(16.dp), + ) + Text( + text = dialogHeaderSubTitle.value, + fontSize = 16.sp, + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Medium, + color = colorResource(id = R.color.silabs_primary_text), + modifier = Modifier + .padding(16.dp) + .fillMaxWidth(), + ) + TextField( + textStyle = TextStyle(fontSize = 20.sp), + modifier = Modifier.padding(16.dp), + enabled = isDownload.value, + value = ipAddress, + onValueChange = { + if (it.length <= 15) { + ipAddress = it + } + + }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + label = { Text(stringResource(R.string.enter_ip_address)) }, + colors = TextFieldDefaults.textFieldColors( + unfocusedLabelColor = colorResource(R.color.silabs_black), + focusedLabelColor = colorResource(R.color.silabs_dark_blue), + textColor = colorResource(R.color.silabs_black), // Text color + backgroundColor = Color.LightGray, // Background color + placeholderColor = Color.Gray, // Placeholder text color + cursorColor = colorResource(R.color.silabs_dark_blue), // Cursor color + focusedIndicatorColor = colorResource(R.color.silabs_dark_blue), // Focused indicator color + unfocusedIndicatorColor = colorResource(id = R.color.silabs_dark_gray_text) // Unfocused indicator color + ) + ) + + val maxChar = 4 + TextField( + textStyle = TextStyle(fontSize = 20.sp), + modifier = Modifier.padding(16.dp), + value = portNumber, + onValueChange = { + if (it.length <= maxChar) + portNumber = it + }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + label = { + Text(context.getString(R.string.enter_port_number)) + }, + colors = TextFieldDefaults.textFieldColors( + unfocusedLabelColor = colorResource(R.color.silabs_black), + focusedLabelColor = colorResource(R.color.silabs_dark_blue), + textColor = colorResource(R.color.silabs_black), // Text color + backgroundColor = Color.LightGray, // Background color + placeholderColor = Color.Gray, // Placeholder text color + cursorColor = colorResource(R.color.silabs_dark_blue), // Cursor color + focusedIndicatorColor = colorResource(R.color.silabs_dark_blue), // Focused indicator color + unfocusedIndicatorColor = colorResource(id = R.color.silabs_dark_gray_text) // Unfocused indicator color + ) + ) + + + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { + TextButton( + onClick = { + dialogState.value = false + isConfirmCalled.value = false + }, + modifier = Modifier.padding(8.dp), + ) { + Text( + text = context.getString(R.string.matter_cancel), + color = colorResource(id = R.color.silabs_dark_blue), + fontSize = 18.sp + ) + } + + var isValidIp = false + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + isValidIp = InetAddresses.isNumericAddress(ipAddress) + } else { + isValidIp = Patterns.IP_ADDRESS.matcher(ipAddress).matches() + } + Button( + onClick = { + if (/*isValidIp + && */portNumber.length >= maxChar) { + dialogState.value = false + showThroughputDetailScreen( + userSelectedFeature, + ipAddress, + portNumber + ) + isConfirmCalled.value = true + } else { + Toast.makeText( + context, + getString(R.string.please_enter_valid_port_number), + Toast.LENGTH_LONG + ).show() + } + }, + shape = RoundedCornerShape(8.dp), // Rounded corners with a 16 dp radius + colors = ButtonDefaults.buttonColors(colorResource(R.color.silabs_dark_blue)), + modifier = Modifier.padding(8.dp) + ) { + Text( + text = context.getString(R.string.dialog_start_server), + color = Color.White, + fontSize = 18.sp + ) + } + } + } + } + } + } + } + + + private fun getLocalIpAddress(context: Context): String { + val wifiManager = + (context.getSystemService(Context.WIFI_SERVICE) as WifiManager) + val wifiInfo = wifiManager.connectionInfo + val ipInt = wifiInfo.ipAddress + return InetAddress.getByAddress( + ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ipInt).array() + ).hostAddress + } +} + diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/adapter/WifiThroughputAdapter.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/adapter/WifiThroughputAdapter.kt new file mode 100644 index 00000000..c32a3ee5 --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/adapter/WifiThroughputAdapter.kt @@ -0,0 +1,65 @@ +package com.siliconlabs.bledemo.features.demo.wifi_throughput.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.features.demo.wifi_throughput.model.WifiThroughputData + +class WifiThroughputAdapter(private val list: MutableList) : RecyclerView.Adapter() { + val myData = mutableListOf() + + fun submitList(newData: MutableList) { + myData.clear() + myData.addAll(newData) + notifyDataSetChanged() + } + + /** + * Provide a reference to the type of views that you are using + * (custom ViewHolder) + */ + class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val interval_tv: TextView + val transfer_rate_tv: TextView + val bandwidth_rate_tv: TextView + + init { + // Define click listener for the ViewHolder's View + interval_tv = view.findViewById(R.id.interval) + transfer_rate_tv = view.findViewById(R.id.transfer_rate) + bandwidth_rate_tv = view.findViewById(R.id.bandwidth) + + } + } + + override fun onCreateViewHolder( + viewGroup: ViewGroup, + viewType: Int + ): ViewHolder { + val view = LayoutInflater.from(viewGroup.context) + .inflate(R.layout.wifi_throughput_log_row, viewGroup, false) + + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + // Get element from your dataset at this position and replace the + // contents of the view with that element + val throughPutValues = myData.get(position).split(",").toTypedArray() + + holder.interval_tv.text = throughPutValues.get(0) + holder.transfer_rate_tv.text = throughPutValues.get(1) + holder.bandwidth_rate_tv.text = "${throughPutValues.get(2)} Mbits/sec" + /* holder.transfer_rate_tv.visibility = View.GONE + holder.bandwidth_rate_tv.visibility = View.GONE*/ + + } + + override fun getItemCount(): Int { + return myData.size + } +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/fragments/WifiThroughPutDetailScreen.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/fragments/WifiThroughPutDetailScreen.kt new file mode 100644 index 00000000..e780dfcf --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/fragments/WifiThroughPutDetailScreen.kt @@ -0,0 +1,335 @@ +package com.siliconlabs.bledemo.features.demo.wifi_throughput.fragments + +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.text.Html +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.text.HtmlCompat +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.transition.Visibility +import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.FragmentWifiThroughPutDetailScreenBinding +import com.siliconlabs.bledemo.features.demo.throughput.views.SpeedView +import com.siliconlabs.bledemo.features.demo.wifi_throughput.activities.WifiThroughputActivity +import com.siliconlabs.bledemo.features.demo.wifi_throughput.adapter.WifiThroughputAdapter +import com.siliconlabs.bledemo.features.demo.wifi_throughput.utils.ThroughputUtils +import com.siliconlabs.bledemo.features.demo.wifi_throughput.viewmodel.WifiThroughputViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class WifiThroughPutDetailScreen : Fragment() { + private var mBundle : Bundle? = null + private lateinit var binding: FragmentWifiThroughPutDetailScreenBinding + + private val throughPutUnitsArray = + arrayListOf( + "0", + "12.5 Mbps", + "25 Mbps", + "37.5 Mbps", + "50 Mbps", + "62.5 Mbps", + "75 Mbps", + "87.5 Mbps", + "100 Mbps" + ) + private val viewModel : WifiThroughputViewModel by viewModels() + var list: MutableList = mutableListOf() + var throughPutTestModeDownload = false + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + super.onCreateView(inflater, container, savedInstanceState) + // Inflate the layout for this fragment + binding = FragmentWifiThroughPutDetailScreenBinding.inflate(inflater, container, false) + binding.wifiSpeedView.setUnitsArray(throughPutUnitsArray) + binding.finalResultLl.visibility = View.GONE + binding.headers.visibility = View.GONE + + mBundle = arguments + + + updateThroughPutSpeed() + + binding.wifiTpMainLayout.setOnClickListener{ + //Do nothing. It is to avoid background touch. + } + + viewModel.isConnceted() + .observe(viewLifecycleOwner) { + if (it){ + binding.connectionStatus.visibility = View.GONE + }else{ + binding.connectionStatus.visibility = View.VISIBLE + } + } + + val customAdapter = WifiThroughputAdapter(list) + val recyclerView = binding.incrementalLog + recyclerView.layoutManager = LinearLayoutManager(requireContext()) + recyclerView.adapter = customAdapter + viewModel.updateLogsPerSecond().observe(viewLifecycleOwner){ + customAdapter.submitList(it) + recyclerView.visibility = View.VISIBLE + if (it.isNotEmpty()){ + binding.headers.visibility = View.VISIBLE + recyclerView.smoothScrollToPosition(it.size - 1) + } + } + + startThroughPutServer() + + binding.start.setOnClickListener { + binding.finalResultLl.visibility = View.GONE + binding.headers.visibility = View.GONE + binding.start.isEnabled = false + startThroughPutServer() + } + + binding.cancel.setOnClickListener { + //dialog?.dismiss() + viewModel.stop() + var fm : FragmentManager = requireActivity().supportFragmentManager + fm.popBackStack() + } + + viewModel.updateFinalResult().observe(viewLifecycleOwner){ + if (null != it && it.isNotEmpty()){ + binding.wifiSpeedView.updateSpeed( getProgress(0f), + getSpeedAsString(0f), + getUnitAsString(0f), + getTestMode()) + binding.start.isEnabled = true + binding.finalResultLl.visibility = View.VISIBLE + if (throughPutTestModeDownload){ + val finalBytesSent = "${getString(R.string.total_bytes_received)}: ${it.get("transfer")}" + binding.finalBytesSent.text = Html.fromHtml(finalBytesSent,HtmlCompat.FROM_HTML_MODE_LEGACY) + }else{ + val totalBytesSent = "${getString(R.string.total_bytes_sent)}: ${it.get("transfer")}" + binding.finalBytesSent.text = Html.fromHtml(totalBytesSent,HtmlCompat.FROM_HTML_MODE_LEGACY) + } + val throughPutAchieved = "${getString(R.string.throughput_achieved)}: @ ${it.get("bandwidth")} ${getString(R.string.mbps_in_30_successfully)}" + binding.finalAcheievedBandvidth.text = Html.fromHtml(throughPutAchieved,HtmlCompat.FROM_HTML_MODE_LEGACY) + } + } + + viewModel.handleException().observe(viewLifecycleOwner){ + if (it){ + binding.start.isEnabled = true + recyclerView.visibility = View.GONE + // binding.finalResultLl.visibility = View.GONE + Toast.makeText(context, + getString(R.string.connection_couldn_t_be_established), Toast.LENGTH_LONG).show() + viewModel.stop() + requireActivity().supportFragmentManager.popBackStack() + (activity as? WifiThroughputActivity)?.updateActionBarTitle(requireContext().getString(R.string.wifi_title_Throughput)) + } + } + + viewModel.handleTlsZeroBytes().observe(viewLifecycleOwner){ + if (it){ + binding.start.isEnabled = true + recyclerView.visibility = View.GONE + // binding.finalResultLl.visibility = View.GONE + Toast.makeText(context, + getString(R.string.ensure_right_combination_of_fw_is_flashed), Toast.LENGTH_LONG).show() + viewModel.stop() + requireActivity().supportFragmentManager.popBackStack() + (activity as? WifiThroughputActivity)?.updateActionBarTitle(requireContext().getString(R.string.wifi_title_Throughput)) + } + } + + return binding.root + } + + private fun startThroughPutServer() { + viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { + mBundle?.let { + if (it.getString(ThroughputUtils.throughPutType)?.equals( + ThroughputUtils.THROUGHPUT_TYPE_TCP_UPLOAD, + ignoreCase = true + ) == true + ) { + throughPutTestModeDownload = false + //set the actionBar title + (activity as? WifiThroughputActivity)?.updateActionBarTitle(it.getString(ThroughputUtils.throughPutType).toString()) + binding.connectionStatus.visibility = View.VISIBLE + if(binding.connectionStatus.isVisible){ + binding.connectionStatusTv.text = getString(R.string.waiting_for_client_to_connect) + } + viewModel.tcpClient( + it.getString(ThroughputUtils.ipAddress)!!, + it.getString(ThroughputUtils.portNumber)!!.toInt() + ) + } else if (it.getString(ThroughputUtils.throughPutType)?.equals( + ThroughputUtils.THROUGHPUT_TYPE_TCP_DOWNLOAD, + ignoreCase = true + ) == true + ) { + //set the actionBar title + (activity as? WifiThroughputActivity)?.updateActionBarTitle(it.getString(ThroughputUtils.throughPutType).toString()) + throughPutTestModeDownload = true + binding.connectionStatus.visibility = View.VISIBLE + if(binding.connectionStatus.isVisible){ + binding.connectionStatusTv.text = getString(R.string.wifi_throughput_connecting_status) + } + + viewModel.tcpServer(it.getString(ThroughputUtils.portNumber)!!.toInt()) + } else if (it.getString(ThroughputUtils.throughPutType)?.equals( + ThroughputUtils.THROUGHPUT_TYPE_UDP_UPLOAD, + ignoreCase = true + ) == true + ) { + //set the actionBar title + (activity as? WifiThroughputActivity)?.updateActionBarTitle(it.getString(ThroughputUtils.throughPutType).toString()) + throughPutTestModeDownload = false + binding.connectionStatus.visibility = View.VISIBLE + if(binding.connectionStatus.isVisible){ + binding.connectionStatusTv.text = getString(R.string.waiting_for_client_to_connect) + binding.connectionStatusTv.visibility = View.GONE + } + + viewModel.udpClient( + it.getString(ThroughputUtils.ipAddress)!!, + it.getString(ThroughputUtils.portNumber)!!.toInt() + ) + } else if (it.getString(ThroughputUtils.throughPutType)?.equals( + ThroughputUtils.THROUGHPUT_TYPE_UDP_DOWNLOAD, + ignoreCase = true + ) == true + ) { + //set the actionBar title + (activity as? WifiThroughputActivity)?.updateActionBarTitle(it.getString(ThroughputUtils.throughPutType).toString()) + throughPutTestModeDownload = true + binding.connectionStatus.visibility = View.VISIBLE + if(binding.connectionStatus.isVisible){ + binding.connectionStatusTv.text = getString(R.string.wifi_throughput_connecting_status) + //binding.connectionStatusTv.visibility = View.GONE + } + + viewModel.udpServer(it.getString(ThroughputUtils.portNumber)!!.toInt()) + //ThroughputUtils.receiUDP() + } else if (it.getString(ThroughputUtils.throughPutType)?.equals( + ThroughputUtils.THROUGHPUT_TYPE_TLS_UPLOAD, + ignoreCase = true + ) == true + ) { + //set the actionBar title + (activity as? WifiThroughputActivity)?.updateActionBarTitle(it.getString(ThroughputUtils.throughPutType).toString()) + throughPutTestModeDownload = false + binding.connectionStatus.visibility = View.VISIBLE + if(binding.connectionStatus.isVisible){ + binding.connectionStatusTv.text = getString(R.string.waiting_for_client_to_connect) + } + + viewModel.startTLSServer( + it.getString(ThroughputUtils.portNumber)!!.toInt(), + requireContext(), + true + ) + } else if (it.getString(ThroughputUtils.throughPutType)?.equals( + ThroughputUtils.THROUGHPUT_TYPE_TLS_DOWNLOAD, + ignoreCase = true + ) == true + ) { + //set the actionBar title + (activity as? WifiThroughputActivity)?.updateActionBarTitle(it.getString(ThroughputUtils.throughPutType).toString()) + throughPutTestModeDownload = true + binding.connectionStatus.visibility = View.VISIBLE + if(binding.connectionStatus.isVisible){ + binding.connectionStatusTv.text = getString(R.string.waiting_for_client_to_connect) + } + + viewModel.startTLSServer( + it.getString(ThroughputUtils.portNumber)!!.toInt(), + requireContext(), + false + ) + } else { + /* Toast.makeText(requireContext(), "Working in progress", Toast.LENGTH_LONG) + .show()*/ + } + } + } + } + + private fun updateThroughPutSpeed() { + lifecycleScope.launch { + withContext(Dispatchers.Main) { + viewModel.updateSpeed() + .observe(viewLifecycleOwner) { + if (it.toString().equals("Infinity", ignoreCase = true) || it.toString() + .equals("Nan", ignoreCase = true) + ) + return@observe + + + binding.wifiSpeedView.updateSpeed( + getWifiThroughPutProgress(it), + getSpeedAsString(it), + getUnitAsString(it), + getTestMode() + ) + } + } + } + } + + private fun getProgress(speed: Float):Int { + //println("Speed : $speed") + return ((speed / 20.0) * 100).toInt() + + // return (speed / 20.0).toInt() + } + + private fun getWifiThroughPutProgress(speed: Float):Int { + return ((speed / 100.0) * 100).toInt() + } + + private fun getSpeedAsString(speed: Float): String { + /* return if (speed >= 1000000) { + (speed / 1000000.0).toString() + } else { + (speed / 100.0).toString() + }*/ + //println("Speed getSpeedAsString : $speed") + + return String.format("%.2f", speed) + } + + private fun getUnitAsString(speed: Float): String { + /*return if (speed >= 1000000) { + "Mbps" + } else { + "kbps" + }*/ + return "Mbps" + } + + private fun getTestMode(): SpeedView.Mode { + if (throughPutTestModeDownload){ + return SpeedView.Mode.DOWNLOAD + } + return SpeedView.Mode.UPLOAD + } + + override fun onDestroy() { + super.onDestroy() + viewModel.stop() + } +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/model/WifiThroughputData.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/model/WifiThroughputData.kt new file mode 100644 index 00000000..b952bdf7 --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/model/WifiThroughputData.kt @@ -0,0 +1,9 @@ +package com.siliconlabs.bledemo.features.demo.wifi_throughput.model + +data class WifiThroughputData(var seconds: String, + var transferRate: String, + var bandwidth:String){ + +} + + diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/utils/ThroughputUtils.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/utils/ThroughputUtils.kt new file mode 100644 index 00000000..5971bf89 --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/utils/ThroughputUtils.kt @@ -0,0 +1,248 @@ +package com.siliconlabs.bledemo.features.demo.wifi_throughput.utils + +import android.content.Context +import com.siliconlabs.bledemo.R +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.net.DatagramPacket +import java.net.DatagramSocket +import java.net.InetAddress +import java.net.InetSocketAddress +import java.net.Socket +import java.util.concurrent.TimeUnit + + +object ThroughputUtils { + + enum class WiFiThroughPutFeature(id: Int) { + TCP_RX(1), + TCP_TX(2), + UDP_RX(3), + UDP_TX(4), + TLS_RX(5), + TLS_TX(6) + } + + var throughPutType : String = "throughputType" + var ipAddress : String = "ipAddress" + var portNumber : String = "portNumber" + + const val THROUGHPUT_TYPE_TCP_UPLOAD = "TCP Upload" + const val THROUGHPUT_TYPE_UDP_UPLOAD = "UDP Upload" + const val THROUGHPUT_TYPE_TLS_UPLOAD = "TLS Upload" + const val THROUGHPUT_TYPE_TCP_DOWNLOAD = "TCP Download" + const val THROUGHPUT_TYPE_UDP_DOWNLOAD = "UDP Download" + const val THROUGHPUT_TYPE_TLS_DOWNLOAD = "TLS Download" + + + var port: Int = 5005 + var running: Boolean = false + var socket: DatagramSocket? = null + + fun getTitle(key : Int, context: Context) : String{ + return when(key){ + 0 -> context.getString(R.string.tcp_receive_title) + 1 -> context.getString(R.string.tcp_send_title) + 2 -> context.getString(R.string.udp_receive_title) + 3 -> context.getString(R.string.udp_send_title) + 4 -> context.getString(R.string.tls_receive_title) + 5 -> context.getString(R.string.tls_send_title) + else -> "Nothing" + } + } + + fun isThroughPutTypeDownload(key : Int) : Boolean{ + return when(key){ + 0, 2, 4, 5 -> true + 1, 3 -> false + else -> false + } + } + + fun sendEvent() { + val BYTES_TO_SEND = 536870912.0 //317470020// Example value, set your actual byte count + val UDP_BUFFER_SIZE = 1470 // Example buffer size, set accordingly + val TEST_TIMEOUT: Long = 10000 // Timeout in milliseconds + val dataBuffer = ByteArray(UDP_BUFFER_SIZE) // Example data buffer + val serverAddress: InetAddress + var socket: DatagramSocket? = null + var totalBytesSent = 0 + var sentBytes: Int + var fail = 0 + var pass = 0 + val SEVER_IP = "192.168.1.194" + val buffer = ByteArray(1470) + for (i in buffer.indices) { + buffer[i] = ('A'.code + (i % 26)).toByte() + } + val startTime = System.nanoTime() + + try { + serverAddress = InetAddress.getByName(SEVER_IP) // Replace with actual server address + socket = DatagramSocket() + socket.soTimeout = TEST_TIMEOUT.toInt() + val start = System.currentTimeMillis() + + while (totalBytesSent < BYTES_TO_SEND) { + println("Total bytes sent so far : $totalBytesSent") + val packet = DatagramPacket( + buffer, + buffer.size, + serverAddress, + 5005 + ) // Replace 9876 with actual server port + socket.send(packet) + val now = System.currentTimeMillis() + + if ((now - start) > TEST_TIMEOUT) { + println( + """ + Time Out: ${now - start} + """.trimIndent() + ) + break + } + sentBytes = packet.length + if (sentBytes < 0) { + fail++ + } else { + pass++ + } + if (sentBytes > 0) { + totalBytesSent += sentBytes + } + } + } catch (e: Exception) { + e.printStackTrace() + } finally { + if (socket != null && !socket.isClosed) { + socket.close() + } + } + val endTime = System.nanoTime() + val timeTakenInSeconds = TimeUnit.NANOSECONDS.toSeconds(endTime - startTime) + val bandwidthInMbps = (totalBytesSent * 8.0) / timeTakenInSeconds / 1_000_000 + println("Bandwidth: $bandwidthInMbps Mbps") + /*println("Total Bytes Sent: $totalBytesSent") + println("Pass: $pass") + println("Fail: $fail")*/ + } + + fun receiveUDPData() { + val LISTENING_PORT = 5005 // Replace with actual listening port + val TEST_TIMEOUT = longArrayOf(60000) // Timeout in milliseconds + val hasDataReceived = booleanArrayOf(false) + val bytesRead = longArrayOf(0) + val start: Long + val now: Long + var serverSocket: DatagramSocket? = null + try { + serverSocket = DatagramSocket() + println( + """ + + Socket ID: ${serverSocket.localPort} + """.trimIndent() + ) + //System.out.println("\nListening on Local Port " + LISTENING_PORT); + start = System.currentTimeMillis() + serverSocket.reuseAddress = true + if (!serverSocket.isBound) serverSocket.bind(InetSocketAddress(5005)) + + val finalServerSocket: DatagramSocket = serverSocket + val receiverThread = Thread { + val receiveBuffer = + ByteArray(1470) // Adjust buffer size as needed + while (!hasDataReceived[0]) { + try { + val receivePacket = + DatagramPacket(receiveBuffer, receiveBuffer.size) + finalServerSocket.receive(receivePacket) + bytesRead[0] += receivePacket.length.toLong() + hasDataReceived[0] = + true // Assuming we stop after first receive for simplicity + } catch (e: IOException) { + e.printStackTrace() + } + } + } + receiverThread.start() + receiverThread.join(TEST_TIMEOUT[0]) + now = System.currentTimeMillis() + println("\nUDP_RX Async Throughput test finished") + println( + """ + + Total bytes received: ${bytesRead[0]} + """.trimIndent() + ) + measureAndPrintThroughput(bytesRead[0], (now - start)) + } catch (e: IOException) { + e.printStackTrace() + } catch (e: InterruptedException) { + e.printStackTrace() + } catch (e: Exception) { + e.printStackTrace() + } finally { + /*if (serverSocket != null && !serverSocket.isClosed()) { + serverSocket.close(); + }*/ + } + } + + private fun measureAndPrintThroughput(totalBytesReceived: Long, duration: Long) { + val throughput = (totalBytesReceived / duration.toDouble()) * 1000 // bytes per second + println("Throughput: $throughput bytes/sec") + } + + fun receiUDP() { + running = true + var totalBytesReceived =0L + + Thread { + try { + var now: Long + socket = DatagramSocket(port) + val start = System.currentTimeMillis() + + println("UDP Server started on port $port") + + val buffer = ByteArray(1470) + while (running) { + val packet = DatagramPacket(buffer, buffer.size) + socket!!.receive(packet) + totalBytesReceived += packet.length + println( + "Total packets receieved is : $totalBytesReceived" + ) + val data = + String(packet.data, 0, packet.length) + /* println( + "Received: " + data + " from " + packet.address.hostAddress + )*/ + now = System.currentTimeMillis() + + measureAndPrintThroughput(packet.length.toLong(), (now - start)) + // Process the received data and send a response if needed + val response = "Echo: $data" + val responsePacket = DatagramPacket( + response.toByteArray(), + response.toByteArray().size, + packet.address, + packet.port + ) + socket!!.send(responsePacket) + } + } catch (e: IOException) { + System.err.println("Error: " + e.message) + } finally { + if (socket != null) { + running = false + socket!!.close() + } + println("UDP Server stopped") + } + }.start() + } +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/viewmodel/WifiThroughputViewModel.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/viewmodel/WifiThroughputViewModel.kt new file mode 100644 index 00000000..b6d1273c --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_throughput/viewmodel/WifiThroughputViewModel.kt @@ -0,0 +1,720 @@ +package com.siliconlabs.bledemo.features.demo.wifi_throughput.viewmodel + +import android.content.Context +import android.system.Os.socket +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.siliconlabs.bledemo.R +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import java.io.BufferedInputStream +import java.io.DataInputStream +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.net.ConnectException +import java.net.DatagramPacket +import java.net.DatagramSocket +import java.net.InetAddress +import java.net.InetSocketAddress +import java.net.ServerSocket +import java.net.Socket +import java.net.SocketTimeoutException +import java.security.KeyStore +import java.util.Timer +import java.util.TimerTask +import java.util.concurrent.TimeUnit +import javax.net.ssl.KeyManagerFactory +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLServerSocket +import javax.net.ssl.SSLServerSocketFactory + + +class WifiThroughputViewModel : ViewModel() { + var bytesCountPerSec: Float = 0.0F + private var timerTask: TimerTask? = null + private var timer: Timer? = null + var count = -1 + private val _updateSpeed = MutableLiveData() + private var finalThroughPut = 0 + private var _perSecondLog = MutableLiveData>() + private var _updateFinalPackets = MutableLiveData>() + var finalBandwidth: Float = 0.0F + //Tcp Server + var totalBytesReceived = 0L + var running: Boolean = false + private var serverSocket: ServerSocket? = null + private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) + private var _handleException = MutableLiveData() + private var _tlsHandleZeroBytes = MutableLiveData() + + private var isExceptionOccured = false + val mobileServerSocket = ServerSocket() + var isConncetedToClient = MutableLiveData() + var udpSocket = DatagramSocket() + + init { + _perSecondLog.value = mutableListOf() + _updateFinalPackets.value = mutableMapOf() + + } + fun updateSpeed():LiveData{ + return _updateSpeed + } + + fun updateLogsPerSecond():LiveData>{ + return _perSecondLog + } + + fun updateFinalResult(): LiveData>{ + return _updateFinalPackets + } + + fun handleException(): MutableLiveData { + return _handleException + } + + fun handleTlsZeroBytes(): MutableLiveData{ + return _tlsHandleZeroBytes + } + + fun isConnceted(): MutableLiveData { + return isConncetedToClient + } + + fun tcpClient(ipAddress : String, portNumber : Int){ + println("TCP client started!!!") + isExceptionOccured = false + count = -1 + finalThroughPut = 0 + val TEST_TIMEOUT: Long = 30000 // Timeout in milliseconds + val BYTES_TO_SEND = 536870912.0 //317470020// Example value, set your actual byte count + val ip = ipAddress // Replace with your server's IP address + val port = portNumber // Replace with your server's port + val buffer = ByteArray(10240) + var socket : Socket? = null + var outputStream : OutputStream? = null + var inputStream : InputStream? = null + var totalBytesTransferred = 0L + var startTime = 0L + var start = 0L + var endTime : Long = 0 + var isTimerStarted = false + try{ + socket = Socket(ip, port) + socket.soTimeout = 30000 + + outputStream = socket.getOutputStream() + inputStream = socket.getInputStream() + + while (totalBytesTransferred < BYTES_TO_SEND) { + if(!isTimerStarted){ + isTimerStarted = true + startTime = System.nanoTime() + start = System.currentTimeMillis() + startTimer() + } + + outputStream.write(buffer) + viewModelScope.launch(Dispatchers.Main) { + isConncetedToClient.value = true + } + totalBytesTransferred += buffer.size + addBytesToCount(buffer.size) + + val now = System.currentTimeMillis() + if ((now - start) > TEST_TIMEOUT || count >= 30) { + println( + """ + Time Out: ${now - start} + """.trimIndent() + ) + break + } + } + endTime = System.nanoTime() + val timeTakenInSeconds = TimeUnit.NANOSECONDS.toSeconds(endTime - startTime) + println("Time taken : " + timeTakenInSeconds) + val throughputInMbps = + (totalBytesTransferred * 8.0) / timeTakenInSeconds / 1_000_000 + println("TCP Throughput total bytes : $totalBytesTransferred") + + println("TCP Throughput: $throughputInMbps Mbps") + + inputStream?.close() + outputStream?.close() + cancelTimer() + }catch (e: ConnectException){ + endTime = System.nanoTime() + println("TCP Throughput: $e") + isExceptionOccured = true + cancelTimer() + } catch (e: Exception){ + endTime = System.nanoTime() + println("TCP Throughput: $e") + //isExceptionOccured = true + pollTimeoutExceptionHandling(e) + cancelTimer() + }finally { + isTimerStarted = false + if(!isExceptionOccured){ + val timeTakenInSeconds = TimeUnit.NANOSECONDS.toSeconds(endTime - startTime) + val throughputInMbps = + (totalBytesTransferred * 8.0) / timeTakenInSeconds / 1_000_000 + //val throughputInMbps = (totalBytesTransferred/(1024*1024)) / timeTakenInSeconds / 1_000_000 + println("Time taken finally: " + timeTakenInSeconds) + + println("TCP Throughput total bytes finally : $totalBytesTransferred") + + println("TCP Throughput finally: $throughputInMbps Mbps") + + socket?.close() + println("TCP Throughput finally : socket connection closed") + cancelTimer() + _updateFinalPackets.value?.put("transfer", totalBytesTransferred.toString()) + _updateFinalPackets.value?.put("bandwidth", String.format("%.2f", throughputInMbps).toString()) + viewModelScope.launch(Dispatchers.Main) { + _updateFinalPackets.value = _updateFinalPackets.value + } + _perSecondLog.value?.clear() + }else{ + viewModelScope.launch(Dispatchers.Main) { + _handleException.value = isExceptionOccured + } + } + } + + } + + private inner class PeriodicSpeedUpdate : TimerTask() { + override fun run() { + count++ + viewModelScope.launch(Dispatchers.Main) { + val throughputInMBps : Float = bytesCountPerSec//bytesToKB(bytesCountPerSec) + val bandwidth = ((bytesCountPerSec*8.388608)/(1000*1000)).toFloat() + finalBandwidth += bandwidth + + var bandwidthPerSecondInString = bytesToHumanReadableSize(throughputInMBps) + var bandwidthPerSecond = "" + + // removing duplicate whitespace + println("Extra whitespaces removed: " + + bandwidthPerSecondInString.replace("\\s+".toRegex(), " ")) + // removing all the whitespaces + println("After removing all the whitespaces: " + + bandwidthPerSecondInString.replace("\\s+".toRegex(), "")) + if (bandwidthPerSecondInString.contains("kB", ignoreCase = true)){ + println("After removing KB: " + + bandwidthPerSecondInString.replace("kB+".toRegex(), "")) + + bandwidthPerSecond = String.format("%.2f", ((bandwidthPerSecondInString.replace("kB+".toRegex(), "").toFloat()*8388.608)/1000000)) + }else if(bandwidthPerSecondInString.contains("MB", ignoreCase = true)){ + println("After removing MB: " + + bandwidthPerSecondInString.replace("MB+".toRegex(), "")) + + bandwidthPerSecond = String.format("%.2f", (bandwidthPerSecondInString.replace("MB+".toRegex(), "").toFloat()*8.388608)) + }else{ + println("After removing MB: " + + bandwidthPerSecondInString.replace("bytes+".toRegex(), "")) + + bandwidthPerSecond = String.format("%.2f", (bandwidthPerSecondInString.replace("bytes+".toRegex(), "").toFloat()*8.388608)) + } + if (count >= 30 && totalBytesReceived == 0L){ + cancelTimer() + _tlsHandleZeroBytes.value = true + } + if (count > 0){ + _updateSpeed.value = bandwidthPerSecond.toFloat() + _perSecondLog.value?.add(getSecondInfo(count, throughputInMBps, bandwidthPerSecond)) + _perSecondLog.value = _perSecondLog.value + } + bytesCountPerSec = 0F + } + } + } + + fun tcpServer(portNumber : Int) { + running = true + count = -1 + viewModelScope.launch(Dispatchers.Main) { + _perSecondLog.value = mutableListOf() + _perSecondLog.value = _perSecondLog.value + isConncetedToClient.value = false + } + isExceptionOccured = false + mobileServerSocket.soTimeout = 30000 + try { + if (mobileServerSocket.isBound){ + println("isBound true") + return + }else{ + mobileServerSocket.bind(InetSocketAddress(portNumber)) + } + //if (!serverSocket.isBound)serverSocket.bind(InetSocketAddress(portNumber)) + println("Server listening on port $portNumber") + while (running) { + val clientSocket = mobileServerSocket.accept() + handleClient(clientSocket!!) + } + } catch (e: Exception) { + pollTimeoutExceptionHandling(e) + println("Exception ${e.message}") + } finally { + //mobileServerSocket.close() + running = false + try { + if (mobileServerSocket != null && !mobileServerSocket.isClosed()) { + mobileServerSocket.close() + } + } catch (e: IOException) { + // Handle socket closing exceptions + if (isExceptionOccured){ + viewModelScope.launch(Dispatchers.Main) { + _handleException.value = isExceptionOccured + } + } + } + } + } + + fun incrementBytesReceived(bytes: Int) { + totalBytesReceived += bytes + } + + private fun handleClient(clientSocket: Socket) { + println("Client connected: ${clientSocket.inetAddress.hostAddress}") + viewModelScope.launch(Dispatchers.Main) { + isConncetedToClient.value = true + } + startTimer() + val dataInputStream = DataInputStream(BufferedInputStream(clientSocket.getInputStream())) + val startTime = System.nanoTime() + try { + while (true) { + val bytesRead = dataInputStream.read(ByteArray(2460)) + if (bytesRead == -1) break + incrementBytesReceived(bytesRead) + addBytesToCount(bytesRead) + } + } catch (e: IOException) {println("Error handling client: ${e.message}") + } finally { + cancelTimer() + dataInputStream.close() + clientSocket.close() + println("Client disconnected") + // Calculate and report throughput every second + coroutineScope.launch { + val endTime = System.nanoTime() + val timeTakenInSeconds = TimeUnit.NANOSECONDS.toSeconds(endTime - startTime) + val throughputInMbps = (totalBytesReceived * 8.388608) / timeTakenInSeconds / 1_000_000 + println("Server Throughput total time taken : $timeTakenInSeconds") + // Update UI or log the throughput + println("Server Throughput total count : $totalBytesReceived") + println("Server Throughput: $throughputInMbps Mbps") + _updateFinalPackets.value?.put("transfer", totalBytesReceived.toString()) + _updateFinalPackets.value?.put("bandwidth", String.format("%.2f", throughputInMbps).toString()) + viewModelScope.launch(Dispatchers.Main) { + _updateFinalPackets.value = _updateFinalPackets.value + } + _perSecondLog.value?.clear() + totalBytesReceived = 0 + count = -1 + } + } + } + + fun stop() { + coroutineScope.cancel() + serverSocket?.close() + mobileServerSocket.close() + sslServerSocket?.close() + udpSocket?.close() + } + + companion object { + const val DISPLAY_REFRESH_PERIOD: Long = 1000 // in milliseconds + } + + private fun startTimer() { + if (timer == null) { + timer = Timer() + timerTask = PeriodicSpeedUpdate() + timer?.scheduleAtFixedRate(timerTask, + WifiThroughputViewModel.DISPLAY_REFRESH_PERIOD, + WifiThroughputViewModel.DISPLAY_REFRESH_PERIOD + ) + } + } + + private fun cancelTimer() { + bytesCountPerSec = 0F + timerTask?.cancel() + timer?.cancel() + timer?.purge() + timerTask = null + timer = null + } + + fun addBytesToCount(packetSize: Int) { + bytesCountPerSec += packetSize + } + + fun getSecondInfo(count : Int, throughPut: Float, bandwidth: String): String{ + return "$count-${count+1} Sec,${bytesToHumanReadableSize(throughPut)},$bandwidth" + } + + fun bytesToHumanReadableSize(bytes: Float) = when { + bytes >= 1 shl 30 -> "%.1f GB".format(bytes / (1 shl 30)) + bytes >= 1 shl 20 -> "%.1f MB".format(bytes / (1 shl 20)) + bytes >= 1 shl 10 -> "%.0f kB".format(bytes / (1 shl 10)) + else -> "$bytes bytes" + } + + fun udpClient(ipAddress : String, portNumber : Int){ + isExceptionOccured = false + count = -1 + finalThroughPut = 0 + val BYTES_TO_SEND = 536870912.0 //317470020// Example value, set your actual byte count + val TEST_TIMEOUT: Long = 30000 // Timeout in milliseconds + val serverAddress: InetAddress + var socket: DatagramSocket? = null + var totalBytesSent = 0 + var sentBytes: Int + var fail = 0 + var pass = 0 + val SEVER_IP = ipAddress + var endTime : Long = 0 + var totalBytesTransferred = 0L + val buffer = ByteArray(1470) + var isTimerStarted = false + var startTime = System.nanoTime() + + try { + serverAddress = InetAddress.getByName(SEVER_IP) // Replace with actual server address + socket = DatagramSocket() + socket.soTimeout = TEST_TIMEOUT.toInt() + var start = System.currentTimeMillis() + if(!isTimerStarted){ + isTimerStarted = true + startTime = System.nanoTime() + start = System.currentTimeMillis() + startTimer() + } + val packet = DatagramPacket( + buffer, + buffer.size, + serverAddress, + portNumber + ) + while (totalBytesTransferred < BYTES_TO_SEND) { + println("Total bytes sent so far : $totalBytesTransferred") + socket.send(packet) + val now = System.currentTimeMillis() + + if ((now - start) > TEST_TIMEOUT || count >= 30) { + println( + """ + Time Out: ${now - start} + """.trimIndent() + ) + //socket.close() + break + } + sentBytes = packet.length + if (sentBytes < 0) { + println("FAIL - : $fail") + + fail++ + } else { + pass++ + } + totalBytesTransferred += sentBytes + + addBytesToCount(sentBytes) + if (sentBytes > 0) { + totalBytesSent += sentBytes + } + println("Total buffer.size : ${buffer.size}") + + totalBytesTransferred += buffer.size + addBytesToCount(buffer.size) + } + endTime = System.nanoTime() + val timeTakenInSeconds = TimeUnit.NANOSECONDS.toSeconds(endTime - startTime) + val throughputInMbps = + (totalBytesTransferred * 8.0) / timeTakenInSeconds / 1_000_000 + cancelTimer() + } catch (e: Exception) { + e.printStackTrace() + cancelTimer() + socket?.close() + } finally { + if (socket != null && !socket.isClosed) { + socket.close() + } + + if(!isExceptionOccured){ + val timeTakenInSeconds = TimeUnit.NANOSECONDS.toSeconds(endTime - startTime) + val throughputInMbps = + (totalBytesTransferred * 8.0) / timeTakenInSeconds / 1_000_000 + socket?.close() + cancelTimer() + _updateFinalPackets.value?.put("transfer", totalBytesTransferred.toString()) + _updateFinalPackets.value?.put("bandwidth", String.format("%.2f", throughputInMbps).toString()) + viewModelScope.launch(Dispatchers.Main) { + _updateFinalPackets.value = _updateFinalPackets.value + } + // _perSecondLog.value?.clear() + println("throughPut speed data are getting cleared to zero!!!") + }else{ + viewModelScope.launch(Dispatchers.Main) { + _handleException.value = isExceptionOccured + } + } + } + } + + fun udpServer(portNumber : Int){ + val port = portNumber + var totalBytesReceived =0L + + running = true + count = -1 + viewModelScope.launch(Dispatchers.Main) { + _perSecondLog.value = mutableListOf() + _perSecondLog.value = _perSecondLog.value + } + // Coroutine to receive UDP packets + coroutineScope.launch { + DatagramSocket(port).use { socket -> + println("UDP Server listening on port $port") + udpSocket = socket + udpSocket.soTimeout = 30000 + val buffer = ByteArray(1470) + var startTime = 0L + var firstTime = true + try { + while (true) { + val packet = DatagramPacket(buffer, buffer.size) + udpSocket.receive(packet) + + if (firstTime){ + startTime = System.nanoTime() + firstTime = false + } + if (packet.length > 0){ + viewModelScope.launch(Dispatchers.Main) { + isConncetedToClient.value = true + } + udpSocket.soTimeout = 500 + startTimer() + } + println("Server Throughput bytes: ${packet.length}") + totalBytesReceived += packet.length + println("totalBytesReceived : $totalBytesReceived") + + incrementBytesReceived(packet.length) + addBytesToCount(packet.length) + } + } catch (e: Exception) { + cancelTimer() + if (count <= 0){ + pollTimeoutExceptionHandling(e) + } + println("Error handling client: ${e.message}") + } finally { + if (!isExceptionOccured){ + firstTime = true + cancelTimer() + println("Client disconnected") + // Calculate and report throughput every second + coroutineScope.launch { + val endTime = System.nanoTime() + val timeTakenInSeconds = TimeUnit.NANOSECONDS.toSeconds(endTime - startTime) + val throughputInMbps = (totalBytesReceived * 8.0) / timeTakenInSeconds / 1_000_000 + println("Server Throughput total time taken : $timeTakenInSeconds") + // Update UI or log the throughput + _updateFinalPackets.value?.put("transfer", totalBytesReceived.toString()) + _updateFinalPackets.value?.put("bandwidth", String.format("%.2f", throughputInMbps).toString()) + viewModelScope.launch(Dispatchers.Main) { + _updateFinalPackets.value = _updateFinalPackets.value + } + _perSecondLog.value?.clear() + totalBytesReceived = 0 + count = -1 + } + }else{ + viewModelScope.launch(Dispatchers.Main) { + _handleException.value = isExceptionOccured + } + } + } + } + } + } + + fun createSSLContext(context: Context): SSLContext { + val keyStore = KeyStore.getInstance("PKCS12") + val password = "Whyyouneed!#2024".toCharArray() // Use the same password you used to create the .p12 file + + val inputStream: InputStream = context.resources.openRawResource(R.raw.keystore) // Put server.p12 in res/raw + keyStore.load(inputStream, password) + + val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) + keyManagerFactory.init(keyStore, password) + + val sslContext = SSLContext.getInstance("TLS") + sslContext.init(keyManagerFactory.keyManagers, null, null) + + return sslContext + } + + private var sslServerSocket: SSLServerSocket? = null + + fun startTLSServer(port: Int, context: Context, isUpload: Boolean) { + count = -1 + isExceptionOccured = false + println("Port number : $port") + viewModelScope.launch(Dispatchers.Main) { + _perSecondLog.value = mutableListOf() + _perSecondLog.value = _perSecondLog.value + isConncetedToClient.value = false + } + try { + val sslContext = createSSLContext(context) + val factory: SSLServerSocketFactory = sslContext.serverSocketFactory + sslServerSocket = factory.createServerSocket(port) as SSLServerSocket + sslServerSocket?.soTimeout = 30000 + sslServerSocket?.let { + it.useTLSProtocols() + while (true) { + val socket = it.accept() // Wait for client connections + println("Accept done") + viewModelScope.launch(Dispatchers.Main) { + isConncetedToClient.value = true + } + // handleTLSClient(socket) // Handle client in separate thread + if (!isUpload){ + handleClient(socket) + }else{ + uploadData(socket!!) + } + } + } + } catch (e: Exception) { + pollTimeoutExceptionHandling(e) + e.printStackTrace() + }finally { + try { + if (sslServerSocket != null && !sslServerSocket!!.isClosed()) { + sslServerSocket!!.close() + } + } catch (e: IOException) { + // Handle socket closing exceptions + if (isExceptionOccured){ + viewModelScope.launch(Dispatchers.Main) { + _handleException.value = isExceptionOccured + } + } + } + } + } + + private fun SSLServerSocket.useTLSProtocols() { + // Set your required TLS protocols + enabledProtocols = arrayOf("TLSv1.2", "TLSv1.3") // Specify protocols as needed + } + + fun uploadData(socket: Socket){ + isExceptionOccured = false + finalThroughPut = 0 + val TEST_TIMEOUT: Long = 30000 // Timeout in milliseconds + val BYTES_TO_SEND = 536870912.0 //317470020// Example value, set your actual byte count + val buffer = ByteArray(1370) + + var outputStream : OutputStream? = null + var totalBytesTransferred = 0L + var startTime = 0L + var start = 0L + var endTime : Long = 0 + var isTimerStarted = false + try{ + outputStream = socket.getOutputStream() + outputStream!!.write(buffer) + viewModelScope.launch { + delay(800L) + } + while (totalBytesTransferred < BYTES_TO_SEND) { + if(!isTimerStarted){ + isTimerStarted = true + startTime = System.nanoTime() + start = System.currentTimeMillis() + startTimer() + } + outputStream = socket.getOutputStream() + outputStream!!.write(buffer) + totalBytesTransferred += buffer.size + addBytesToCount(buffer.size) + + val now = System.currentTimeMillis() + if ((now - start) > TEST_TIMEOUT || count >= 30) { + println( + """ + Time Out: ${now - start} + """.trimIndent() + ) + break + } + } + endTime = System.nanoTime() + val timeTakenInSeconds = TimeUnit.NANOSECONDS.toSeconds(endTime - startTime) + println("Time taken : " + timeTakenInSeconds) + val throughputInMbps = + (totalBytesTransferred * 8.0) / timeTakenInSeconds / 1_000_000 + println("TCP Throughput total bytes : $totalBytesTransferred") + + println("TCP Throughput: $throughputInMbps Mbps") + + outputStream?.close() + cancelTimer() + }catch (e: Exception){ + endTime = System.nanoTime() + println("TCP Throughput: $e") + //isExceptionOccured = true + cancelTimer() + }finally { + isTimerStarted = false + count = -1 + if(!isExceptionOccured){ + val timeTakenInSeconds = TimeUnit.NANOSECONDS.toSeconds(endTime - startTime) + val throughputInMbps = + (totalBytesTransferred * 8.0) / timeTakenInSeconds / 1_000_000 + socket.close() + println("TCP Throughput finally : socket connection closed") + cancelTimer() + _updateFinalPackets.value?.put("transfer", totalBytesTransferred.toString()) + _updateFinalPackets.value?.put("bandwidth", String.format("%.2f", throughputInMbps).toString()) + viewModelScope.launch(Dispatchers.Main) { + _updateFinalPackets.value = _updateFinalPackets.value + } + _perSecondLog.value?.clear() + }else{ + viewModelScope.launch(Dispatchers.Main) { + _handleException.value = isExceptionOccured + } + } + } + } + + private fun pollTimeoutExceptionHandling(e: Exception) { + if (e is SocketTimeoutException){ + isExceptionOccured = true + } + } +} diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/activities/IOPTestActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/activities/IOPTestActivity.kt index 49ed5d65..0f24386e 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/activities/IOPTestActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/activities/IOPTestActivity.kt @@ -34,6 +34,7 @@ import android.net.Uri import android.os.* import android.text.TextUtils import android.util.Log +import android.view.LayoutInflater import android.view.Menu import android.view.MenuItem import android.view.View @@ -42,7 +43,6 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat.startActivity -import androidx.core.content.FileProvider import androidx.core.view.get import androidx.lifecycle.lifecycleScope import com.siliconlabs.bledemo.R @@ -50,8 +50,8 @@ import com.siliconlabs.bledemo.bluetooth.ble.GattCharacteristic import com.siliconlabs.bledemo.bluetooth.ble.GattService import com.siliconlabs.bledemo.bluetooth.ble.TimeoutGattCallback import com.siliconlabs.bledemo.bluetooth.services.BluetoothService +import com.siliconlabs.bledemo.databinding.ActivityIopTestBinding import com.siliconlabs.bledemo.databinding.DialogShareIopLogBinding -import com.siliconlabs.bledemo.features.scan.browser.dialogs.OtaLoadingDialog import com.siliconlabs.bledemo.features.iop_test.fragments.IOPTestFragment import com.siliconlabs.bledemo.features.iop_test.fragments.IOPTestFragment.Companion.newInstance import com.siliconlabs.bledemo.features.iop_test.models.* @@ -64,11 +64,11 @@ import com.siliconlabs.bledemo.features.iop_test.test_cases.ota.OtaFileManager import com.siliconlabs.bledemo.features.iop_test.test_cases.ota.OtaFileSelectionDialog import com.siliconlabs.bledemo.features.iop_test.test_cases.ota.OtaProgressDialog import com.siliconlabs.bledemo.features.iop_test.utils.ErrorCodes +import com.siliconlabs.bledemo.features.scan.browser.dialogs.OtaLoadingDialog import com.siliconlabs.bledemo.utils.BLEUtils.setNotificationForCharacteristic import com.siliconlabs.bledemo.utils.Converters import com.siliconlabs.bledemo.utils.Notifications import com.siliconlabs.bledemo.utils.UuidConsts -import kotlinx.android.synthetic.main.activity_iop_test.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -180,6 +180,7 @@ class IOPTestActivity : AppCompatActivity() { private var testCaseCount = 0 private var shareMenuItem: MenuItem? = null + private lateinit var binding: ActivityIopTestBinding private val mBondStateReceiver: BroadcastReceiver = object : BroadcastReceiver() { @@ -309,7 +310,12 @@ class IOPTestActivity : AppCompatActivity() { * Start test by item */ private fun startItemTest(item: Int) { - mListener?.scrollViewToPosition(item) + lifecycleScope.launch { + withContext(Dispatchers.Main){ + mListener?.scrollViewToPosition(item) + } + } + mIndexRunning = item isTestRunning = true @@ -829,7 +835,6 @@ class IOPTestActivity : AppCompatActivity() { } - private fun startTest() { isTestRunning = true isTestFinished = false @@ -845,7 +850,7 @@ class IOPTestActivity : AppCompatActivity() { */ private fun updateUIFooter(isRunning: Boolean) { if (!isRunning) { - btn_start_and_stop_test.apply { + binding.btnStartAndStopTest.apply { text = getString(R.string.button_run_test) isEnabled = true backgroundTintList = ColorStateList.valueOf( @@ -860,7 +865,7 @@ class IOPTestActivity : AppCompatActivity() { mBluetoothDevice?.let { removeBond(it) } } } else { - btn_start_and_stop_test?.apply { + binding.btnStartAndStopTest?.apply { text = getString(R.string.button_waiting) backgroundTintList = ColorStateList.valueOf( ContextCompat.getColor( @@ -873,6 +878,7 @@ class IOPTestActivity : AppCompatActivity() { shareMenuItem?.isVisible = false } } + private fun removeBond(device: BluetoothDevice): Boolean { return try { return device::class.java.getMethod("removeBond").invoke(device) as Boolean @@ -882,7 +888,6 @@ class IOPTestActivity : AppCompatActivity() { } - /** * Show information device test and progress test item */ @@ -890,9 +895,9 @@ class IOPTestActivity : AppCompatActivity() { val siliconlabsTestInfo = getSiliconLabsTestInfo() runOnUiThread { if (isInformation || item == POSITION_TEST_CONNECTION) { - tv_fw_name.text = + binding.tvFwName.text = getString(R.string.iop_test_label_fw_name, siliconlabsTestInfo.fwName) - tv_device_name.text = + binding.tvDeviceName.text = getString(R.string.iop_test_label_device_name, siliconlabsTestInfo.phoneName) } val total = siliconlabsTestInfo.totalTestCase.toString() @@ -900,7 +905,7 @@ class IOPTestActivity : AppCompatActivity() { testCaseCount = item + mIndexStartChildrenTest + 1 } Log.d(TAG, "The number of test case $testCaseCount") - tv_progress.text = getString( + binding.tvProgress.text = getString( R.string.iop_test_label_progress_count_test, testCaseCount.toString(), total @@ -969,7 +974,8 @@ class IOPTestActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_iop_test) + binding = ActivityIopTestBinding.inflate(LayoutInflater.from(this)) + setContentView(binding.root) supportActionBar?.apply { setDisplayHomeAsUpEnabled(true) @@ -1029,9 +1035,11 @@ class IOPTestActivity : AppCompatActivity() { } return true } + private fun showCustomDialog() { val dialog = Dialog(this) - val dialogBinding: DialogShareIopLogBinding = DialogShareIopLogBinding.inflate(layoutInflater) + val dialogBinding: DialogShareIopLogBinding = + DialogShareIopLogBinding.inflate(layoutInflater) dialog.setContentView(dialogBinding.root) dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) dialog.setCancelable(true) @@ -1099,7 +1107,7 @@ class IOPTestActivity : AppCompatActivity() { } private fun handleClickEvents() { - btn_start_and_stop_test.setOnClickListener { + binding.btnStartAndStopTest.setOnClickListener { startTest() } } @@ -1216,12 +1224,17 @@ class IOPTestActivity : AppCompatActivity() { } } } ?: run { - Toast.makeText(this, getString(R.string.chosen_file_not_found), Toast.LENGTH_SHORT).show() + Toast.makeText( + this, + getString(R.string.chosen_file_not_found), + Toast.LENGTH_SHORT + ).show() } } } } } + /** * Save log data into File */ @@ -1230,10 +1243,10 @@ class IOPTestActivity : AppCompatActivity() { val outputStream = contentResolver.openOutputStream(uri) outputStream?.use { fOut -> OutputStreamWriter(fOut).use { myOutWriter -> - if (isLogcatData){ + if (isLogcatData) { myOutWriter.write(logs + "\n" + getDataLog()) - }else{ - myOutWriter.write( getDataLog()) + } else { + myOutWriter.write(getDataLog()) } @@ -1689,7 +1702,7 @@ class IOPTestActivity : AppCompatActivity() { val pathFile = getSiliconLabsTestInfo().phoneName pathFile.replace(" ", "") if (isLogcatData) { - return "logcat"+"_" + getDate("EEE MMM dd HH_mm_ss z yyyy") + ".txt" + return "logcat" + "_" + getDate("EEE MMM dd HH_mm_ss z yyyy") + ".txt" } else { return pathFile + "_" + boardName + "_" + getDate("yyyy_MM_dd_HH_mm_ss") + ".txt" @@ -1697,8 +1710,6 @@ class IOPTestActivity : AppCompatActivity() { } - - /** * Get Date currently */ @@ -1712,14 +1723,15 @@ class IOPTestActivity : AppCompatActivity() { private fun getDataLog(): String { return getSiliconLabsTestInfo().toString() } + private fun shareLogDataTestByEmail(uri: Uri) { try { val message = "Please check the attachment to get the log file." val intent = Intent(Intent.ACTION_SEND).apply { type = "text/plain" - if (isLogcatData){ + if (isLogcatData) { putExtra(Intent.EXTRA_SUBJECT, "[Silabs] Application Debug log") - }else{ + } else { putExtra(Intent.EXTRA_SUBJECT, "[Silabs] Test log") } @@ -1735,7 +1747,6 @@ class IOPTestActivity : AppCompatActivity() { } - /** * Perform the test by item test case */ @@ -2080,7 +2091,7 @@ class IOPTestActivity : AppCompatActivity() { when (step) { // write CCCD to different value from the default 3 -> { - Log.d(TAG,"CASE 3: iopPhase3RunTestCaseBonding"+ "Write value 0x00 to CCCD") + Log.d(TAG, "CASE 3: iopPhase3RunTestCaseBonding" + "Write value 0x00 to CCCD") CCCD_value[0] = 1 iopPhase3ExtraDescriptor?.setValue(CCCD_value) mBluetoothGatt?.writeDescriptor(iopPhase3ExtraDescriptor) @@ -2091,17 +2102,20 @@ class IOPTestActivity : AppCompatActivity() { // disconnect 4 -> { - Log.d(TAG,"CASE 4: iopPhase3RunTestCaseBonding"+ "disconnect") + Log.d(TAG, "CASE 4: iopPhase3RunTestCaseBonding" + "disconnect") disconnectGatt(mBluetoothGatt) - } + } 6 -> { mBluetoothGatt?.readDescriptor(iopPhase3ExtraDescriptor) - Log.d(TAG,"CASE 6: iopPhase3RunTestCaseBonding"+ "read CCCD:" + read_CCCD_value[0]) + Log.d( + TAG, + "CASE 6: iopPhase3RunTestCaseBonding" + "read CCCD:" + read_CCCD_value[0] + ) // Mobile read CCCD. (Pass: if the CCCD value is the same as the value written before bond ). if (read_CCCD_value[0].toInt() == 1) { - isCCCDPass=true + isCCCDPass = true } else { isCCCDPass = false updateDataTestFailed(POSITION_TEST_IOP3_SECURITY) @@ -2115,7 +2129,7 @@ class IOPTestActivity : AppCompatActivity() { // Mobile write value 0xBB (0xBB or 0x00) to CCCD. (Pass: if the CCCD value was written in last step). 7 -> { - Log.d(TAG,"CASE 7: iopPhase3RunTestCaseBonding"+ "Write value 0x00 to CCCD") + Log.d(TAG, "CASE 7: iopPhase3RunTestCaseBonding" + "Write value 0x00 to CCCD") CCCD_value[0] = 0 iopPhase3ExtraDescriptor?.setValue(CCCD_value) mBluetoothGatt?.writeDescriptor(iopPhase3ExtraDescriptor) @@ -2124,7 +2138,10 @@ class IOPTestActivity : AppCompatActivity() { 8 -> { mBluetoothGatt?.readDescriptor(iopPhase3ExtraDescriptor) read_CCCD_value = iopPhase3ExtraDescriptor?.getValue()!!.copyOf() - Log.d(TAG,"CASE 8: iopPhase3RunTestCaseBonding"+ "read CCCD:" + read_CCCD_value[0]) + Log.d( + TAG, + "CASE 8: iopPhase3RunTestCaseBonding" + "read CCCD:" + read_CCCD_value[0] + ) // Mobile read CCCD. (Pass: if the CCCD value is the same as the value written before bond ). if (read_CCCD_value[0].toInt() == 0) { isCCCDPass = true @@ -2395,8 +2412,6 @@ class IOPTestActivity : AppCompatActivity() { } - - private val iopCachingRunnable = Runnable { Log.d(TAG, "iopCachingRunnable") iopCachingFailed() diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/adapters/IOPTestViewHolder.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/adapters/IOPTestViewHolder.kt index bb7381b8..5d6904e7 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/adapters/IOPTestViewHolder.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/adapters/IOPTestViewHolder.kt @@ -5,15 +5,16 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.AdapterIopTestBinding import com.siliconlabs.bledemo.features.iop_test.models.ItemTestCaseInfo -import kotlinx.android.synthetic.main.adapter_iop_test.view.* -class IOPTestViewHolder(view: View) : RecyclerView.ViewHolder(view) { - private val ivTestStatus = view.iv_test_status - private val tvTestTitle = view.tv_test_title - private val tvTestDescription = view.tv_test_description - private val tvTestStatus = view.tv_test_status - private val pbTestProgress = view.pb_test_progress + +class IOPTestViewHolder(view: AdapterIopTestBinding) : RecyclerView.ViewHolder(view.root) { + private val ivTestStatus = view.ivTestStatus + private val tvTestTitle = view.tvTestTitle + private val tvTestDescription = view.tvTestDescription + private val tvTestStatus = view.tvTestStatus + private val pbTestProgress = view.pbTestProgress fun bind(info: ItemTestCaseInfo) { tvTestTitle.text = info.titlesTest @@ -32,6 +33,7 @@ class IOPTestViewHolder(view: View) : RecyclerView.ViewHolder(view) { tvTestStatus.setTextColor(context.getColor(R.color.silabs_red)) pbTestProgress.visibility = View.GONE } + 1 -> { ivTestStatus.setBackgroundResource(R.drawable.ic_test_pass) ivTestStatus.visibility = View.VISIBLE @@ -39,12 +41,14 @@ class IOPTestViewHolder(view: View) : RecyclerView.ViewHolder(view) { tvTestStatus.setTextColor(context.getColor(R.color.silabs_blue)) pbTestProgress.visibility = View.GONE } + 2 -> { ivTestStatus.visibility = View.GONE tvTestStatus.visibility = View.GONE pbTestProgress.visibility = View.VISIBLE } + else -> { ivTestStatus.visibility = View.GONE tvTestStatus.visibility = View.VISIBLE @@ -57,7 +61,8 @@ class IOPTestViewHolder(view: View) : RecyclerView.ViewHolder(view) { companion object { fun create(parent: ViewGroup): IOPTestViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_iop_test, parent, false) + val view = + AdapterIopTestBinding.inflate(LayoutInflater.from(parent.context), parent, false) return IOPTestViewHolder(view) } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/dialogs/AboutIopDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/dialogs/AboutIopDialog.kt index b83aa675..fd3ac3d1 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/dialogs/AboutIopDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/dialogs/AboutIopDialog.kt @@ -8,15 +8,22 @@ import android.view.View import android.view.ViewGroup import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.dialog_info_iop.* +import com.siliconlabs.bledemo.databinding.DialogInfoIopBinding + class AboutIopDialog : BaseDialogFragment( - hasCustomWidth = true, - isCanceledOnTouchOutside = true + hasCustomWidth = true, + isCanceledOnTouchOutside = true ) { - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_info_iop, container, false) + private lateinit var binding: DialogInfoIopBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogInfoIopBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -25,9 +32,9 @@ class AboutIopDialog : BaseDialogFragment( } private fun handleClickEvents() { - btn_ok.setOnClickListener { dismiss() } + binding.btnOk.setOnClickListener { dismiss() } - btn_interoperability_test.setOnClickListener { + binding.btnInteroperabilityTest.setOnClickListener { handleClickOnInteroperabilityTest() } } @@ -40,6 +47,7 @@ class AboutIopDialog : BaseDialogFragment( } companion object { - private const val IOP_LINK = "https://www.silabs.com/documents/public/application-notes/an1346-running-ble-iop-test.pdf" + private const val IOP_LINK = + "https://www.silabs.com/documents/public/application-notes/an1346-running-ble-iop-test.pdf" } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/fragments/IOPTestFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/fragments/IOPTestFragment.kt index 8aa49488..ae9e194c 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/fragments/IOPTestFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/fragments/IOPTestFragment.kt @@ -20,18 +20,20 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager +import by.kirich1409.viewbindingdelegate.viewBinding import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.features.iop_test.activities.IOPTestActivity import com.siliconlabs.bledemo.features.iop_test.adapters.IOPTestAdapter import com.siliconlabs.bledemo.features.iop_test.models.IOPTest import com.siliconlabs.bledemo.features.iop_test.models.ItemTestCaseInfo import com.siliconlabs.bledemo.common.other.CardViewListDecoration -import kotlinx.android.synthetic.main.fragment_iop_test.* +import com.siliconlabs.bledemo.databinding.FragmentIopTestBinding import java.util.* class IOPTestFragment : Fragment(R.layout.fragment_iop_test), IOPTestActivity.Listener { private lateinit var testAdapter: IOPTestAdapter private var listItemTest: List = ArrayList() + private val binding by viewBinding(FragmentIopTestBinding::bind) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -46,7 +48,8 @@ class IOPTestFragment : Fragment(R.layout.fragment_iop_test), IOPTestActivity.Li private fun initAdapter() { testAdapter = IOPTestAdapter(listItemTest) - rv_iop_tests.apply { + + binding.rvIopTests.apply { layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) addItemDecoration(CardViewListDecoration()) this.adapter = testAdapter @@ -54,8 +57,8 @@ class IOPTestFragment : Fragment(R.layout.fragment_iop_test), IOPTestActivity.Li } override fun scrollViewToPosition(position: Int) { - rv_iop_tests.post { - rv_iop_tests.smoothScrollToPosition(position) + binding.rvIopTests.post { + binding.rvIopTests.smoothScrollToPosition(position) } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/utils/DialogDeviceInfoFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/utils/DialogDeviceInfoFragment.kt index ae5f1883..8742b29b 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/utils/DialogDeviceInfoFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/utils/DialogDeviceInfoFragment.kt @@ -3,6 +3,8 @@ package com.siliconlabs.bledemo.features.iop_test.utils import android.app.AlertDialog import android.app.Dialog import android.content.DialogInterface +import android.graphics.Color +import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -28,6 +30,10 @@ class DialogDeviceInfoFragment : DialogFragment() { savedInstanceState: Bundle? ): View? { binding = DialogDeviceInfoLayoutBinding.inflate(inflater, container, false) + if (dialog != null && dialog!!.window != null) { + dialog!!.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + dialog!!.setCanceledOnTouchOutside(false) + } return binding.root } @@ -56,30 +62,6 @@ class DialogDeviceInfoFragment : DialogFragment() { binding.btnNegative.text = negativeButtonText } - /*override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(requireActivity()) - - builder.setTitle(title) - builder.setMessage(message) - - if (positiveButtonText != null) { - builder.setPositiveButton(positiveButtonText, positiveClickListener) - } - - if (negativeButtonText != null) { - builder.setNegativeButton(negativeButtonText, negativeClickListener) - } - - val dialog = builder.create() - dialog.setOnShowListener { - dialogShowing = true - } - dialog.setOnDismissListener { - dialogShowing = false - } - - return dialog - }*/ // Builder pattern for setting dialog properties class Builder { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/utils/Utils.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/utils/Utils.kt index feca3c23..9e2c8af0 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/utils/Utils.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/utils/Utils.kt @@ -40,4 +40,5 @@ class Utils { return (capitalize(manufacturer) + "_" + model).replace(" ", "_") } } + } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/active_connections/fragments/ConnectionsFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/active_connections/fragments/ConnectionsFragment.kt index 30378b51..5bccef1d 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/active_connections/fragments/ConnectionsFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/active_connections/fragments/ConnectionsFragment.kt @@ -22,6 +22,7 @@ import com.siliconlabs.bledemo.common.other.CardViewListDecoration import com.siliconlabs.bledemo.home_screen.base.ViewPagerFragment import com.siliconlabs.bledemo.home_screen.base.BaseServiceDependentMainMenuFragment import com.siliconlabs.bledemo.home_screen.base.BluetoothDependent +import com.siliconlabs.bledemo.home_screen.base.NotificationDependent class ConnectionsFragment : BaseServiceDependentMainMenuFragment() { @@ -33,7 +34,9 @@ class ConnectionsFragment : BaseServiceDependentMainMenuFragment() { override fun onAttach(context: Context) { super.onAttach(context) - service = (activity as MainActivity).bluetoothService!! + if(null != (activity as MainActivity).bluetoothService){ + service = (activity as MainActivity).bluetoothService!! + } viewModel = (parentFragment as ViewPagerFragment).getScanFragment().viewModel } @@ -69,6 +72,7 @@ class ConnectionsFragment : BaseServiceDependentMainMenuFragment() { } } + override fun onResume() { super.onResume() service.registerGattCallback(gattCallback) diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/activities/DeviceServicesActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/activities/DeviceServicesActivity.kt index 48833148..9421acf4 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/activities/DeviceServicesActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/activities/DeviceServicesActivity.kt @@ -35,6 +35,7 @@ import android.widget.* import androidx.core.content.ContextCompat.startActivity import androidx.core.view.children import androidx.fragment.app.Fragment +import by.kirich1409.viewbindingdelegate.viewBinding import com.siliconlabs.bledemo.base.activities.BaseActivity import com.siliconlabs.bledemo.bluetooth.ble.ErrorCodes import com.siliconlabs.bledemo.bluetooth.ble.TimeoutGattCallback @@ -45,8 +46,8 @@ import com.siliconlabs.bledemo.features.scan.browser.fragments.* import com.siliconlabs.bledemo.features.scan.browser.models.OtaFileType import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.common.views.FlyInBar +import com.siliconlabs.bledemo.databinding.ActivityDeviceServicesBinding import com.siliconlabs.bledemo.utils.* -import kotlinx.android.synthetic.main.activity_device_services.* import timber.log.Timber import java.io.* import java.util.* @@ -100,13 +101,16 @@ class DeviceServicesActivity : BaseActivity() { private var retryAttempts = 0 - private val hideFabOnScrollChangeListener = OnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY -> - if (scrollY > oldScrollY) { - btn_bond_action.hide() - } else { - btn_bond_action.show() + private lateinit var binding:ActivityDeviceServicesBinding + + private val hideFabOnScrollChangeListener = + OnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY -> + if (scrollY > oldScrollY) { + binding.btnBondAction.hide() + } else { + binding.btnBondAction.show() + } } - } private val remoteServicesFragment = RemoteServicesFragment(hideFabOnScrollChangeListener) private val localServicesFragment = LocalServicesFragment() @@ -115,7 +119,8 @@ class DeviceServicesActivity : BaseActivity() { private val bondStateChangeListener = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) { - val newState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE) + val newState = + intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE) displayBondState(newState) if (newState == BluetoothDevice.BOND_BONDED) { showMessage(getString(R.string.device_bonded_successfully)) @@ -140,7 +145,7 @@ class DeviceServicesActivity : BaseActivity() { override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) { if (viewState == ViewState.IDLE) { super.onReadRemoteRssi(gatt, rssi, status) - runOnUiThread { tv_rssi.text = resources.getString(R.string.n_dBm, rssi) } + runOnUiThread { binding.tvRssi.text = resources.getString(R.string.n_dBm, rssi) } } } @@ -150,15 +155,17 @@ class DeviceServicesActivity : BaseActivity() { when (mtuReadType) { MtuReadType.VIEW_INITIALIZATION -> { MTU = if (status == BluetoothGatt.GATT_SUCCESS) mtu - else DEFAULT_MTU_VALUE + else DEFAULT_MTU_VALUE gatt.requestConnectionPriority(connectionPriority) } + MtuReadType.UPLOAD_INITIALIZATION -> { MTU = if (status == BluetoothGatt.GATT_SUCCESS) mtu - else DEFAULT_MTU_VALUE + else DEFAULT_MTU_VALUE gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH) // optimize upload speed writeOtaControl(OTA_CONTROL_START_COMMAND) } + MtuReadType.USER_REQUESTED -> { if (status == BluetoothGatt.GATT_SUCCESS) { MTU = mtu @@ -183,33 +190,47 @@ class DeviceServicesActivity : BaseActivity() { when (newState) { BluetoothGatt.STATE_CONNECTED -> { if (viewState == ViewState.REBOOTING || - viewState == ViewState.REBOOTING_NEW_FIRMWARE) { + viewState == ViewState.REBOOTING_NEW_FIRMWARE + ) { handler.postDelayed({ bluetoothGatt = null gatt.discoverServices() }, 250) } } + BluetoothGatt.STATE_DISCONNECTED -> { when (viewState) { ViewState.IDLE -> when (status) { 0 -> finish() else -> { - showLongMessage(ErrorCodes.getDeviceDisconnectedMessage(getDeviceName(), status)) + showLongMessage( + ErrorCodes.getDeviceDisconnectedMessage( + getDeviceName(), + status + ) + ) finish() } } + ViewState.REBOOTING -> when (status) { 19 -> { /* Device is reconnecting into ota mode */ showInitializationInfo() } + 0 -> {} /* Device reconnecting for another file upload, let it be */ else -> showErrorDialog(status) } + ViewState.UPLOADING -> showErrorDialog(status) - ViewState.REBOOTING_NEW_FIRMWARE -> { /* Do nothing */ } - else -> { finish() } + ViewState.REBOOTING_NEW_FIRMWARE -> { /* Do nothing */ + } + + else -> { + finish() + } } } } @@ -249,31 +270,34 @@ class DeviceServicesActivity : BaseActivity() { if (status != BluetoothGatt.GATT_SUCCESS) { showErrorDialog(status) if (viewState == ViewState.UPLOADING) viewState = ViewState.IDLE - } - else { + } else { when (characteristic.uuid) { - UuidConsts.OTA_CONTROL -> { when (characteristic.value[0]) { - 0x00.toByte() -> { - if (viewState == ViewState.REBOOTING) { - reloadDeviceIntoOtaMode() - } - else if (viewState == ViewState.INITIALIZING_UPLOAD) { - viewState = ViewState.UPLOADING - startOtaUpload() + UuidConsts.OTA_CONTROL -> { + when (characteristic.value[0]) { + 0x00.toByte() -> { + if (viewState == ViewState.REBOOTING) { + reloadDeviceIntoOtaMode() + } else if (viewState == ViewState.INITIALIZING_UPLOAD) { + viewState = ViewState.UPLOADING + startOtaUpload() + } } - } - 0x03.toByte() -> { - if (viewState == ViewState.UPLOADING) { - viewState = ViewState.IDLE - if (boolFullOTA) { - prepareForNextUpload() - } else { - runOnUiThread { otaProgressDialog?.toggleEndButton(isEnabled = true) } + + 0x03.toByte() -> { + if (viewState == ViewState.UPLOADING) { + viewState = ViewState.IDLE + if (boolFullOTA) { + prepareForNextUpload() + } else { + runOnUiThread { otaProgressDialog?.toggleEndButton(isEnabled = true) } + } } } + + else -> {} } - else -> { } - } } + } + UuidConsts.OTA_DATA -> handleReliableUploadResponse() } } @@ -281,19 +305,30 @@ class DeviceServicesActivity : BaseActivity() { } //CALLBACK ON DESCRIPTOR WRITE - override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { + override fun onDescriptorWrite( + gatt: BluetoothGatt, + descriptor: BluetoothGattDescriptor, + status: Int + ) { super.onDescriptorWrite(gatt, descriptor, status) remoteServicesFragment.updateDescriptorView(descriptor) } //CALLBACK ON DESCRIPTOR READ - override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { + override fun onDescriptorRead( + gatt: BluetoothGatt, + descriptor: BluetoothGattDescriptor, + status: Int + ) { super.onDescriptorRead(gatt, descriptor, status) remoteServicesFragment.updateDescriptorView(descriptor) } //CALLBACK ON CHARACTERISTIC CHANGED VALUE (READ - CHARACTERISTIC NOTIFICATION) - override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { + override fun onCharacteristicChanged( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic + ) { super.onCharacteristicChanged(gatt, characteristic) remoteServicesFragment.updateCharacteristicView(characteristic) } @@ -306,7 +341,8 @@ class DeviceServicesActivity : BaseActivity() { if (status != BluetoothGatt.GATT_SUCCESS) showErrorDialog(status) else { printServicesInfo(gatt) - otaDataCharPresent = (gatt.getService(UuidConsts.OTA_SERVICE)?.getCharacteristic(UuidConsts.OTA_DATA) != null) + otaDataCharPresent = (gatt.getService(UuidConsts.OTA_SERVICE) + ?.getCharacteristic(UuidConsts.OTA_DATA) != null) when (viewState) { ViewState.REFRESHING_SERVICES -> { @@ -315,6 +351,7 @@ class DeviceServicesActivity : BaseActivity() { }, GATT_FETCH_ON_SERVICE_DISCOVERED_DELAY) viewState = ViewState.IDLE } + ViewState.IDLE -> { handler.postDelayed({ initServicesFragments(bluetoothGatt?.services.orEmpty()) @@ -322,16 +359,18 @@ class DeviceServicesActivity : BaseActivity() { gatt.requestMtu(INITIALIZATION_MTU_VALUE) }, GATT_FETCH_ON_SERVICE_DISCOVERED_DELAY) } + ViewState.REBOOTING -> { viewState = ViewState.INITIALIZING_UPLOAD mtuReadType = MtuReadType.UPLOAD_INITIALIZATION bluetoothGatt?.requestMtu(INITIALIZATION_MTU_VALUE) } + ViewState.REBOOTING_NEW_FIRMWARE -> { bluetoothGatt?.readCharacteristic(getDeviceNameCharacteristic()) } - else -> { } + else -> {} } } } @@ -348,16 +387,20 @@ class DeviceServicesActivity : BaseActivity() { hideOtaLoadingDialog() otafile = readChosenFile() pack = 0 - if (reliable) { setupMtuDivisible() } + if (reliable) { + setupMtuDivisible() + } hideOtaProgressDialog() - showOtaProgressDialog(OtaProgressDialog.OtaInfo( + showOtaProgressDialog( + OtaProgressDialog.OtaInfo( prepareFilename(), otafile?.size, if (reliable) mtuDivisible else MTU, doubleStepUpload, if (doubleStepUpload) stackPath != "" else true - )) + ) + ) Thread { Thread.sleep(DIALOG_DELAY) @@ -423,7 +466,8 @@ class DeviceServicesActivity : BaseActivity() { */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_device_services) + binding = ActivityDeviceServicesBinding.inflate(LayoutInflater.from(this)) + setContentView(binding.root) bluetoothDevice = intent.getParcelableExtra(CONNECTED_DEVICE) @@ -444,18 +488,21 @@ class DeviceServicesActivity : BaseActivity() { add(R.id.services_fragment_container, remoteServicesFragment) }.commit() - services_bottom_nav.setOnNavigationItemSelectedListener { item -> + + binding.servicesBottomNav.setOnNavigationItemSelectedListener { item -> (when (item.itemId) { R.id.services_nav_remote -> { toggleRemoteActions(isRemoteFragmentOn = true) supportActionBar?.title = getDeviceName() remoteServicesFragment } + R.id.services_nav_local -> { toggleRemoteActions(isRemoteFragmentOn = false) supportActionBar?.title = bluetoothService?.bluetoothAdapter?.name localServicesFragment } + else -> null })?.let { newFragment -> supportFragmentManager.beginTransaction().apply { @@ -469,12 +516,13 @@ class DeviceServicesActivity : BaseActivity() { } private fun toggleRemoteActions(isRemoteFragmentOn: Boolean) { - btn_bond_action.visibility = - if (isRemoteFragmentOn) View.VISIBLE - else View.GONE - connection_info.visibility = - if (isRemoteFragmentOn) View.VISIBLE - else View.GONE + binding.btnBondAction.visibility = + if (isRemoteFragmentOn) View.VISIBLE + else View.GONE + + binding.connectionInfo.visibility = + if (isRemoteFragmentOn) View.VISIBLE + else View.GONE toggleMenuItemsVisibility(isRemoteFragmentOn) } @@ -493,27 +541,35 @@ class DeviceServicesActivity : BaseActivity() { } private fun setupUiListeners() { - tv_ota_firmware.setOnClickListener { + binding.tvOtaFirmware.setOnClickListener { if (isUiCreated) checkForOtaCharacteristic() } - btn_bond_action.setOnClickListener { - bluetoothGatt?.device?.let { when (it.bondState) { - BluetoothDevice.BOND_BONDED -> askUnbondDevice(it) - BluetoothDevice.BOND_NONE -> it.createBond() - else -> { } - } } + binding.btnBondAction.setOnClickListener { + bluetoothGatt?.device?.let { + when (it.bondState) { + BluetoothDevice.BOND_BONDED -> askUnbondDevice(it) + BluetoothDevice.BOND_NONE -> it.createBond() + else -> {} + } + } } } private fun checkForOtaCharacteristic() { if (getOtaControlCharacteristic() != null) showOtaConfigDialog() - else OtaCharacteristicMissingDialog().show(supportFragmentManager, "ota_characteristic_missing_dialog") + else OtaCharacteristicMissingDialog().show( + supportFragmentManager, + "ota_characteristic_missing_dialog" + ) } private fun registerReceivers() { registerReceiver(bluetoothReceiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)) - registerReceiver(bondStateChangeListener, IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) + registerReceiver( + bondStateChangeListener, + IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED) + ) } @@ -522,47 +578,54 @@ class DeviceServicesActivity : BaseActivity() { when (state) { BluetoothDevice.BOND_BONDED -> { - tv_bond_state.text = getString(R.string.bonded) - btn_bond_action.apply { + + binding.tvBondState.text = getString(R.string.bonded) + binding.btnBondAction.apply { isEnabled = true text = getString(R.string.delete_bond) setIsActionOn(true) } } + BluetoothDevice.BOND_BONDING -> { - tv_bond_state.text = getString(R.string.bonding) - btn_bond_action.apply { + binding.tvBondState.text = getString(R.string.bonding) + binding.btnBondAction.apply { isEnabled = false text = getString(R.string.bonding) } } + BluetoothDevice.BOND_NONE -> { - tv_bond_state.text = getString(R.string.not_bonded) - btn_bond_action.apply { + binding.tvBondState.text = getString(R.string.not_bonded) + binding.btnBondAction.apply { isEnabled = true text = getString(R.string.create_bond) setIsActionOn(false) } } - else -> { } + + else -> {} } } - private fun getOtaControlCharacteristic() : BluetoothGattCharacteristic? { - return bluetoothGatt?.getService(UuidConsts.OTA_SERVICE)?.getCharacteristic(UuidConsts.OTA_CONTROL) + private fun getOtaControlCharacteristic(): BluetoothGattCharacteristic? { + return bluetoothGatt?.getService(UuidConsts.OTA_SERVICE) + ?.getCharacteristic(UuidConsts.OTA_CONTROL) } - private fun getDeviceNameCharacteristic() : BluetoothGattCharacteristic? { - return bluetoothGatt?.getService(UuidConsts.GENERIC_ACCESS)?.getCharacteristic(UuidConsts.DEVICE_NAME) + private fun getDeviceNameCharacteristic(): BluetoothGattCharacteristic? { + return bluetoothGatt?.getService(UuidConsts.GENERIC_ACCESS) + ?.getCharacteristic(UuidConsts.DEVICE_NAME) } private fun setActivityResult() { setResult(REFRESH_INFO_RESULT_CODE, Intent().apply { putExtra(CONNECTED_DEVICE, bluetoothDevice) - putExtra(CONNECTION_STATE, - if (bluetoothService?.isGattConnected(bluetoothDevice?.address) == true) BluetoothGatt.STATE_CONNECTED - else BluetoothGatt.STATE_DISCONNECTED + putExtra( + CONNECTION_STATE, + if (bluetoothService?.isGattConnected(bluetoothDevice?.address) == true) BluetoothGatt.STATE_CONNECTED + else BluetoothGatt.STATE_DISCONNECTED ) }) } @@ -620,18 +683,21 @@ class DeviceServicesActivity : BaseActivity() { R.id.show_logs -> showLogFragment() R.id.request_priority -> { ConnectionRequestDialog(connectionPriority, connectionRequestCallback) - .show(supportFragmentManager, CONNECTION_REQUEST_DIALOG_FRAGMENT) + .show(supportFragmentManager, CONNECTION_REQUEST_DIALOG_FRAGMENT) } + R.id.request_mtu -> { mtuRequestDialog = MtuRequestDialog(MTU, mtuRequestCallback).also { it.show(supportFragmentManager, MTU_REQUEST_DIALOG_FRAGMENT) } } + android.R.id.home -> { onBackPressed() return true } - else -> { } + + else -> {} } return super.onOptionsItemSelected(item) } @@ -640,8 +706,9 @@ class DeviceServicesActivity : BaseActivity() { super.onBackPressed() if (isLogFragmentOn) { - fragment_container.visibility = View.GONE - services_container.visibility = View.VISIBLE + + binding.fragmentContainer.visibility = View.GONE + binding.servicesContainer.visibility = View.VISIBLE isLogFragmentOn = false supportActionBar?.title = bluetoothDevice?.name toggleMenuItemsVisibility(areVisible = true) @@ -652,12 +719,16 @@ class DeviceServicesActivity : BaseActivity() { override fun onConnectionPriorityRequested(priority: Int) { bluetoothGatt?.requestConnectionPriority(priority) connectionPriority = priority - showMessage(getString(when (priority) { - BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER -> R.string.connection_priority_low - BluetoothGatt.CONNECTION_PRIORITY_BALANCED -> R.string.connection_priority_balanced - BluetoothGatt.CONNECTION_PRIORITY_HIGH -> R.string.connection_priority_high - else -> R.string.connection_priority_low - })) + showMessage( + getString( + when (priority) { + BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER -> R.string.connection_priority_low + BluetoothGatt.CONNECTION_PRIORITY_BALANCED -> R.string.connection_priority_balanced + BluetoothGatt.CONNECTION_PRIORITY_HIGH -> R.string.connection_priority_high + else -> R.string.connection_priority_low + } + ) + ) } } @@ -674,8 +745,9 @@ class DeviceServicesActivity : BaseActivity() { addToBackStack(null) }.commit() - fragment_container.visibility = View.VISIBLE - services_container.visibility = View.GONE + binding.fragmentContainer.visibility = View.VISIBLE + binding.servicesContainer + binding.servicesContainer.visibility = View.GONE toggleMenuItemsVisibility(areVisible = false) isLogFragmentOn = true } @@ -784,8 +856,14 @@ class DeviceServicesActivity : BaseActivity() { private fun sendFileChooserIntent() { Intent(Intent.ACTION_GET_CONTENT) .apply { type = "*/*" } - .also { startActivityForResult(Intent.createChooser(it, - getString(R.string.ota_choose_file)), FILE_CHOOSER_REQUEST_CODE) } + .also { + startActivityForResult( + Intent.createChooser( + it, + getString(R.string.ota_choose_file) + ), FILE_CHOOSER_REQUEST_CODE + ) + } } private fun hideOtaProgressDialog() { @@ -890,23 +968,43 @@ class DeviceServicesActivity : BaseActivity() { j++ if (j >= MTU - 3 || i >= (datathread.size - 1)) { var wait = System.nanoTime() - val charac = bluetoothGatt?.getService(UuidConsts.OTA_SERVICE)?.getCharacteristic( - UuidConsts.OTA_DATA - ) + val charac = + bluetoothGatt?.getService(UuidConsts.OTA_SERVICE)?.getCharacteristic( + UuidConsts.OTA_DATA + ) charac?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE val progress = ((i + 1).toFloat() / datathread.size) * 100 - val bitrate = (((i + 1) * (8.0)).toFloat() / (((wait - start) / 1000000.0).toFloat())) + val bitrate = + (((i + 1) * (8.0)).toFloat() / (((wait - start) / 1000000.0).toFloat())) if (j < MTU - 3) { val end = ByteArray(j) System.arraycopy(value, 0, end, 0, j) - Log.d("Progress", "sent " + (i + 1) + " / " + datathread.size + " - " + String.format("%.1f", progress) + " % - " + String.format("%.2fkbit/s", bitrate) + " - " + Converters.bytesToHexWhitespaceDelimited(end)) + Log.d( + "Progress", + "sent " + (i + 1) + " / " + datathread.size + " - " + String.format( + "%.1f", + progress + ) + " % - " + String.format( + "%.2fkbit/s", + bitrate + ) + " - " + Converters.bytesToHexWhitespaceDelimited(end) + ) runOnUiThread { otaProgressDialog?.updateDataProgress(progress.toInt()) } charac?.value = end } else { j = 0 - Log.d("Progress", "sent " + (i + 1) + " / " + datathread.size + " - " + String.format("%.1f", progress) + " % - " + String.format("%.2fkbit/s", bitrate) + " - " + Converters.bytesToHexWhitespaceDelimited(value)) + Log.d( + "Progress", + "sent " + (i + 1) + " / " + datathread.size + " - " + String.format( + "%.1f", + progress + ) + " % - " + String.format( + "%.2fkbit/s", + bitrate + ) + " - " + Converters.bytesToHexWhitespaceDelimited(value) + ) runOnUiThread { otaProgressDialog?.updateDataProgress(progress.toInt()) } @@ -960,7 +1058,12 @@ class DeviceServicesActivity : BaseActivity() { } else writearray[j] = otafile!![i] } pgss = ((pack + last).toFloat() / (otafile?.size!! - 1)) * 100 - Log.d("characte", "last: " + pack + " / " + (pack + last) + " : " + Converters.bytesToHexWhitespaceDelimited(writearray)) + Log.d( + "characte", + "last: " + pack + " / " + (pack + last) + " : " + Converters.bytesToHexWhitespaceDelimited( + writearray + ) + ) } else { var j = 0 writearray = ByteArray(mtuDivisible) @@ -969,9 +1072,15 @@ class DeviceServicesActivity : BaseActivity() { j++ } pgss = ((pack + mtuDivisible).toFloat() / (otafile?.size!! - 1)) * 100 - Log.d("characte", "pack: " + pack + " / " + (pack + mtuDivisible) + " : " + Converters.bytesToHexWhitespaceDelimited(writearray)) + Log.d( + "characte", + "pack: " + pack + " / " + (pack + mtuDivisible) + " : " + Converters.bytesToHexWhitespaceDelimited( + writearray + ) + ) } - val charac = bluetoothGatt?.getService(UuidConsts.OTA_SERVICE)?.getCharacteristic(UuidConsts.OTA_DATA) + val charac = bluetoothGatt?.getService(UuidConsts.OTA_SERVICE) + ?.getCharacteristic(UuidConsts.OTA_DATA) charac?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT charac?.value = writearray bluetoothGatt?.writeCharacteristic(charac) @@ -979,17 +1088,19 @@ class DeviceServicesActivity : BaseActivity() { val bitrate = 8 * pack.toFloat() / waiting_time if (pack > 0) { handler.post { - runOnUiThread { otaProgressDialog?.let { + runOnUiThread { + otaProgressDialog?.let { it.updateDataRate(bitrate) it.updateDataProgress(pgss.toInt()) - } } + } + } } } else { otatime = System.currentTimeMillis() } } - private fun readChosenFile() : ByteArray? { + private fun readChosenFile(): ByteArray? { return try { val file: File if (stackPath != "" && doubleStepUpload) { @@ -1011,7 +1122,7 @@ class DeviceServicesActivity : BaseActivity() { } } - private fun prepareFilename() : String { + private fun prepareFilename(): String { return if (stackPath != "" && doubleStepUpload) { val last = stackPath.lastIndexOf(File.separator) getString(R.string.ota_filename_s, stackPath.substring(last).removePrefix("/")) @@ -1047,9 +1158,10 @@ class DeviceServicesActivity : BaseActivity() { private fun showCharacteristicLoadingAnimation(barLabel: String) { runOnUiThread { - btn_bond_action.visibility = View.GONE - tv_bond_state_with_rssi.visibility = View.GONE - fly_in_bar.apply { + binding.btnBondAction.visibility = View.GONE + + binding.tvBondStateWithRssi.visibility = View.GONE + binding.flyInBar.apply { setOnClickListener { /* this onclicklistener prevents services and characteristics from user interaction before ui is loaded*/ } visibility = View.VISIBLE startFlyInAnimation(barLabel) @@ -1059,17 +1171,22 @@ class DeviceServicesActivity : BaseActivity() { private fun hideCharacteristicLoadingAnimation() { runOnUiThread { - fly_in_bar.startFlyOutAnimation(object : FlyInBar.Callback { + + binding.flyInBar.startFlyOutAnimation(object : FlyInBar.Callback { override fun onFlyOutAnimationEnded() { - fly_in_bar.visibility = View.GONE - btn_bond_action.visibility = View.VISIBLE - tv_bond_state_with_rssi.visibility = View.VISIBLE + binding.flyInBar.visibility = View.GONE + binding.btnBondAction.visibility = View.VISIBLE + binding.tvBondStateWithRssi.visibility = View.VISIBLE } }) } } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_PERMISSION) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { @@ -1126,6 +1243,7 @@ class DeviceServicesActivity : BaseActivity() { } } + @SuppressLint("Range") private fun getFileName(uri: Uri?): String? { var result: String? = null if ((uri?.scheme == "content")) { @@ -1150,7 +1268,7 @@ class DeviceServicesActivity : BaseActivity() { return filename?.toUpperCase(Locale.getDefault())?.contains(".GBL")!! } - private fun getDeviceName() : String { + private fun getDeviceName(): String { return bluetoothDevice?.let { if (TextUtils.isEmpty(it.name)) getString(R.string.not_advertising_shortcut) else it.name @@ -1211,7 +1329,8 @@ class DeviceServicesActivity : BaseActivity() { private const val RECONNECTION_DELAY = 4000L // device needs to reboot private const val OTA_CONTROL_START_DELAY = 200L // needed to avoid error status 135 private const val OTA_CONTROL_END_DELAY = 500L - private const val CACHE_REFRESH_DELAY = 500L // no callback available, so give some time to refresh + private const val CACHE_REFRESH_DELAY = + 500L // no callback available, so give some time to refresh private const val DIALOG_DELAY = 500L // give time for progress dialog to bind layout private const val CONNECTION_REQUEST_DIALOG_FRAGMENT = "connection_request_dialog_fragment" diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/activities/UuidDictionaryActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/activities/UuidDictionaryActivity.kt index d1de0d71..d4b5b8fe 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/activities/UuidDictionaryActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/activities/UuidDictionaryActivity.kt @@ -6,18 +6,22 @@ import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.viewpager2.adapter.FragmentStateAdapter +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.tabs.TabLayoutMediator import com.siliconlabs.bledemo.features.scan.browser.dialogs.AboutUuidDictionaryDialog import com.siliconlabs.bledemo.features.scan.browser.fragments.CharacteristicMappingsFragment import com.siliconlabs.bledemo.features.scan.browser.fragments.ServiceMappingsFragment import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.activity_uuid_dictionary.* +import com.siliconlabs.bledemo.databinding.ActivityUuidDictionaryBinding -class UuidDictionaryActivity : AppCompatActivity() { +class UuidDictionaryActivity : AppCompatActivity() { + //private val binding by viewBinding(ActivityUuidDictionaryBinding::bind) //activity_uuid_dictionary + private lateinit var binding: ActivityUuidDictionaryBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_uuid_dictionary) + binding = ActivityUuidDictionaryBinding.inflate(layoutInflater) + setContentView(binding.root) setupViewPager() setupActionBar() @@ -31,8 +35,12 @@ class UuidDictionaryActivity : AppCompatActivity() { override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.mappings_about -> { - AboutUuidDictionaryDialog().show(supportFragmentManager, "about_mappings_dictionary_dialog") + AboutUuidDictionaryDialog().show( + supportFragmentManager, + "about_mappings_dictionary_dialog" + ) } + android.R.id.home -> onBackPressed() else -> { } @@ -41,9 +49,11 @@ class UuidDictionaryActivity : AppCompatActivity() { } private fun setupViewPager() { - view_pager2.adapter = ViewPagerAdapter() - TabLayoutMediator(uuid_dictionary_tab_layout, view_pager2) { tab, position -> + binding.viewPager2.adapter = ViewPagerAdapter() + + + TabLayoutMediator(binding.uuidDictionaryTabLayout, binding.viewPager2) { tab, position -> tab.text = when (position) { 0 -> getString(R.string.title_services) 1 -> getString(R.string.title_characteristics) diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/adapters/DebugModeDeviceAdapter.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/adapters/DebugModeDeviceAdapter.kt index 1ad7e527..f658f2ae 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/adapters/DebugModeDeviceAdapter.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/adapters/DebugModeDeviceAdapter.kt @@ -31,8 +31,8 @@ import java.util.concurrent.TimeUnit import kotlin.math.roundToLong class DebugModeDeviceAdapter( - private var chosenDevices: MutableList, - private val debugModeCallback: DebugModeCallback + private var chosenDevices: MutableList, + private val debugModeCallback: DebugModeCallback ) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { @@ -59,22 +59,37 @@ class DebugModeDeviceAdapter( private fun setupUiListeners(holder: ViewHolder, viewBinding: AdapterBrowserDeviceBinding) { viewBinding.apply { - expandArrow.setOnClickListener { RecyclerViewUtils.withProperAdapterPosition(holder) { pos -> - debugModeCallback.toggleViewExpansion(pos) - } } - connectionBtn.setOnClickListener { RecyclerViewUtils.withProperAdapterPosition(holder) { pos -> - with(chosenDevices[pos].deviceInfo) { - when (connectionState) { - BluetoothDeviceInfo.ConnectionState.CONNECTED -> debugModeCallback.disconnectDevice(pos, this.device) - BluetoothDeviceInfo.ConnectionState.DISCONNECTED -> debugModeCallback.connectToDevice(pos, this) - else -> { } + expandArrow.setOnClickListener { + println("SICONNECT") + RecyclerViewUtils.withProperAdapterPosition(holder) { pos -> + debugModeCallback.toggleViewExpansion(pos) + } + } + connectionBtn.setOnClickListener { + RecyclerViewUtils.withProperAdapterPosition(holder) { pos -> + with(chosenDevices[pos].deviceInfo) { + when (connectionState) { + BluetoothDeviceInfo.ConnectionState.CONNECTED -> debugModeCallback.disconnectDevice( + pos, + this.device + ) + + BluetoothDeviceInfo.ConnectionState.DISCONNECTED -> debugModeCallback.connectToDevice( + pos, + this + ) + + else -> {} + } } } - } } - favoriteBtn.setOnClickListener { RecyclerViewUtils.withProperAdapterPosition(holder) { pos -> - val clickedDevice = chosenDevices[pos].deviceInfo - toggleFavoriteDevice(clickedDevice) - } } + } + favoriteBtn.setOnClickListener { + RecyclerViewUtils.withProperAdapterPosition(holder) { pos -> + val clickedDevice = chosenDevices[pos].deviceInfo + toggleFavoriteDevice(clickedDevice) + } + } } } @@ -83,17 +98,22 @@ class DebugModeDeviceAdapter( notifyItemInserted(itemCount - 1) } - fun updateDevices(newList: List, withMoves: Boolean = false) { - val listDiff = DiffUtil.calculateDiff(DiffCallback( + fun updateDevices( + newList: List, + withMoves: Boolean = false + ) { + val listDiff = DiffUtil.calculateDiff( + DiffCallback( chosenDevices.toList(), newList - ), withMoves) + ), withMoves + ) chosenDevices = getDeepCopyList(newList).toMutableList() listDiff.dispatchUpdatesTo(this) } - private fun getDeepCopyList(list: List) : List { + private fun getDeepCopyList(list: List): List { return mutableListOf().apply { list.forEach { add(it.copy(deviceInfo = it.deviceInfo.clone())) } } @@ -108,8 +128,8 @@ class DebugModeDeviceAdapter( } private class DiffCallback( - private val oldList: List, - private val newList: List + private val oldList: List, + private val newList: List ) : DiffUtil.Callback() { override fun getOldListSize(): Int { @@ -136,16 +156,16 @@ class DebugModeDeviceAdapter( } private class PayloadChange( - val oldItem: ScanFragmentViewModel.BluetoothInfoViewState, - val newItem: ScanFragmentViewModel.BluetoothInfoViewState + val oldItem: ScanFragmentViewModel.BluetoothInfoViewState, + val newItem: ScanFragmentViewModel.BluetoothInfoViewState ) private class AdvertisementData(val rows: List>) @SuppressLint("MissingPermission") class ViewHolder( - private val context: Context, - private val viewBinding: AdapterBrowserDeviceBinding + private val context: Context, + private val viewBinding: AdapterBrowserDeviceBinding ) : RecyclerView.ViewHolder(viewBinding.root) { fun bind(viewInfoState: ScanFragmentViewModel.BluetoothInfoViewState) { @@ -155,16 +175,18 @@ class DebugModeDeviceAdapter( viewBinding.apply { manufacturerIcon.setImageDrawable(getManufacturerIcon(info.manufacturer)) deviceName.text = - if (info.name.isEmpty()) itemView.context.getString(R.string.not_advertising_shortcut) - else info.name + if (info.name.isEmpty()) itemView.context.getString(R.string.not_advertising_shortcut) + else info.name tvDeviceAddress.text = context.getString(R.string.string_placeholder, info.address) rssi.text = itemView.context.getString(R.string.unit_value_dbm, info.rssi) - tvInterval.text = itemView.context.getString(R.string.unit_value_ms, info.intervalNanos / 1000000) - deviceType.text = itemView.context.getString(info.bleFormat?.nameResId ?: R.string.unspecified) + tvInterval.text = + itemView.context.getString(R.string.unit_value_ms, info.intervalNanos / 1000000) + deviceType.text = + itemView.context.getString(info.bleFormat?.nameResId ?: R.string.unspecified) tvIsConnectable.text = itemView.context.getString( - if (info.isConnectable) R.string.connectible - else R.string.non_connectible + if (info.isConnectable) R.string.connectible + else R.string.non_connectible ) favoriteBtn.isChecked = info.isFavorite tvIsBonded.text = getBondedStateText(info.device.bondState) @@ -187,7 +209,8 @@ class DebugModeDeviceAdapter( if (newInfo.rssi != oldInfo.rssi) rssi.text = context.getString(R.string.unit_value_dbm, newInfo.rssi) if (newInfo.intervalNanos != oldInfo.intervalNanos) - tvInterval.text = context.getString(R.string.unit_value_ms, newInfo.intervalNanos / 1000000) + tvInterval.text = + context.getString(R.string.unit_value_ms, newInfo.intervalNanos / 1000000) if (newInfo.device.bondState != oldInfo.device.bondState) tvIsBonded.text = getBondedStateText(newInfo.device.bondState) if (newInfo.connectionState != oldInfo.connectionState) { @@ -197,10 +220,16 @@ class DebugModeDeviceAdapter( favoriteBtn.isChecked = newInfo.isFavorite } if (oldInfo.rawData != newInfo.rawData) { - deviceType.text = itemView.context.getString(newInfo.bleFormat?.nameResId ?: R.string.unspecified) + deviceType.text = itemView.context.getString( + newInfo.bleFormat?.nameResId ?: R.string.unspecified + ) manufacturerIcon.setImageDrawable(getManufacturerIcon(newInfo.manufacturer)) - - if (newState.isInfoExpanded) addAdvertsDataToContainer(generateAdvertData(newInfo)) + println("SiCONNECT --newState.isInfoExpanded:${newState.isInfoExpanded}") + if (newState.isInfoExpanded) addAdvertsDataToContainer( + generateAdvertData( + newInfo + ) + ) } } } @@ -210,11 +239,13 @@ class DebugModeDeviceAdapter( if (deviceInfo.isConnectable) { visibility = View.VISIBLE setActionButtonState(deviceInfo.connectionState) - text = context.getString( when (deviceInfo.connectionState) { - BluetoothDeviceInfo.ConnectionState.CONNECTED -> R.string.button_disconnect - BluetoothDeviceInfo.ConnectionState.CONNECTING -> R.string.button_connecting - BluetoothDeviceInfo.ConnectionState.DISCONNECTED -> R.string.button_connect - }) + text = context.getString( + when (deviceInfo.connectionState) { + BluetoothDeviceInfo.ConnectionState.CONNECTED -> R.string.button_disconnect + BluetoothDeviceInfo.ConnectionState.CONNECTING -> R.string.button_connecting + BluetoothDeviceInfo.ConnectionState.DISCONNECTED -> R.string.button_connect + } + ) } else { visibility = View.GONE } @@ -223,25 +254,29 @@ class DebugModeDeviceAdapter( private fun toggleDetails(shouldShowDetails: Boolean) { viewBinding.advertisementContainer.visibility = - if (shouldShowDetails) View.VISIBLE - else View.GONE + if (shouldShowDetails) View.VISIBLE + else View.GONE viewBinding.expandArrow.setState(shouldShowDetails) } - private fun generateAdvertData(deviceInfo: BluetoothDeviceInfo) : AdvertisementData { + private fun generateAdvertData(deviceInfo: BluetoothDeviceInfo): AdvertisementData { val rows: MutableList> = ArrayList() if (!deviceInfo.scanInfo?.isLegacy!!) { - rows.add(Pair(context.resources.getString(R.string.Bluetooth_5_Advertising_Extension), - prepareBluetooth5AdvertExtensionData(deviceInfo.scanInfo!!))) + rows.add( + Pair( + context.resources.getString(R.string.Bluetooth_5_Advertising_Extension), + prepareBluetooth5AdvertExtensionData(deviceInfo.scanInfo!!) + ) + ) } deviceInfo.advertData.forEach { val data = it?.split(ScanRecordParser.SPLIT.toRegex())?.toTypedArray()!! val dataLabel = data[0] val dataValue = - if (data.size > 1) data[1] - else "" + if (data.size > 1) data[1] + else "" rows.add(Pair(dataLabel, dataValue)) } @@ -249,49 +284,59 @@ class DebugModeDeviceAdapter( BleFormat.I_BEACON -> iBeaconAdv(deviceInfo, rows) BleFormat.EDDYSTONE -> eddystoneAdv(deviceInfo, rows) BleFormat.ALT_BEACON -> altBeaconAdv(deviceInfo, rows) - else -> { } + else -> {} } return AdvertisementData(rows) } - private fun getManufacturerIcon(manufacturer: BluetoothDeviceInfo.DeviceManufacturer) : Drawable? { - return ContextCompat.getDrawable(context, when (manufacturer) { - BluetoothDeviceInfo.DeviceManufacturer.WINDOWS -> R.drawable.redesign_ic_scanned_device_windows - BluetoothDeviceInfo.DeviceManufacturer.UNKNOWN -> R.drawable.redesign_ic_bluetooth_with_background - }) + private fun getManufacturerIcon(manufacturer: BluetoothDeviceInfo.DeviceManufacturer): Drawable? { + return ContextCompat.getDrawable( + context, when (manufacturer) { + BluetoothDeviceInfo.DeviceManufacturer.WINDOWS -> R.drawable.redesign_ic_scanned_device_windows + BluetoothDeviceInfo.DeviceManufacturer.UNKNOWN -> R.drawable.redesign_ic_bluetooth_with_background + } + ) } - private fun getBondedStateText(bondState: Int) : String { + private fun getBondedStateText(bondState: Int): String { return context.getString( - if (bondState == BluetoothDevice.BOND_BONDED) R.string.scanned_device_bonded - else R.string.scanned_device_not_bonded + if (bondState == BluetoothDevice.BOND_BONDED) R.string.scanned_device_bonded + else R.string.scanned_device_not_bonded ) } private fun prepareBluetooth5AdvertExtensionData(scanResult: ScanResultCompat): String { return StringBuilder().apply { append(context.getString(R.string.Data_Status_colon)) - append(context.getString( + append( + context.getString( if (scanResult.dataStatus == 0) R.string.advertising_extension_status_complete else R.string.advertising_extension_status_truncated - )) + ) + ) append("
") append(context.getString(R.string.Primary_PHY_colon)) - append(context.getString( + append( + context.getString( if (scanResult.primaryPhy == 1) R.string.advertising_extension_phy_le_1m else R.string.advertising_extension_phy_le_coded - )) + ) + ) append("
") append(context.getString(R.string.Secondary_PHY_colon)) - append(context.getString(when (scanResult.secondaryPhy) { - 1 -> R.string.advertising_extension_phy_le_1m - 2 -> R.string.advertising_extension_phy_le_2m - 3 -> R.string.advertising_extension_phy_le_coded - else -> R.string.advertising_extension_phy_unused - })) + append( + context.getString( + when (scanResult.secondaryPhy) { + 1 -> R.string.advertising_extension_phy_le_1m + 2 -> R.string.advertising_extension_phy_le_2m + 3 -> R.string.advertising_extension_phy_le_coded + else -> R.string.advertising_extension_phy_unused + } + ) + ) append("
") append(context.getString(R.string.Advertising_Set_ID)) @@ -314,16 +359,35 @@ class DebugModeDeviceAdapter( }.toString() } - private fun validateEddyStoneServiceData(beacon: Beacon, deviceAddress: String, serviceData: ByteArray?) { + private fun validateEddyStoneServiceData( + beacon: Beacon, + deviceAddress: String, + serviceData: ByteArray? + ) { if (serviceData == null) { val err = "Null Eddystone service data" beacon.frameStatus.nullServiceData = err return } when (serviceData[0]) { - Constants.UID_FRAME_TYPE -> UidValidator.validate(deviceAddress, serviceData, beacon) - Constants.TLM_FRAME_TYPE -> TlmValidator.validate(deviceAddress, serviceData, beacon) - Constants.URL_FRAME_TYPE -> UrlValidator.validate(deviceAddress, serviceData, beacon) + Constants.UID_FRAME_TYPE -> UidValidator.validate( + deviceAddress, + serviceData, + beacon + ) + + Constants.TLM_FRAME_TYPE -> TlmValidator.validate( + deviceAddress, + serviceData, + beacon + ) + + Constants.URL_FRAME_TYPE -> UrlValidator.validate( + deviceAddress, + serviceData, + beacon + ) + else -> { val err = String.format("Invalid frame type byte %02X", serviceData[0]) beacon.frameStatus.invalidFrameType = err @@ -337,24 +401,31 @@ class DebugModeDeviceAdapter( val title = it.first val text = Html.fromHtml(it.second).toString() val serviceItemContainer = DetailsRow(context, title, text) - viewBinding.advertisementContainer.addView(serviceItemContainer, + viewBinding.advertisementContainer.addView(serviceItemContainer.binding.root, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) } } - private fun iBeaconAdv(deviceInfo: BluetoothDeviceInfo, rows: MutableList>) { - IBeaconInfo.getIBeaconInfo(deviceInfo.scanInfo?.scanRecord?.bytes!!)?.let { beaconInfo -> - val info = StringBuilder().apply { - append("Minor: ").append(beaconInfo.minor).append("
") - append("Major: ").append(beaconInfo.major).append("
") - append("UUID: ").append(beaconInfo.uuid).append("
") - append("RSSI at 1m: ").append(beaconInfo.power) - }.toString() - rows.add(Pair("iBeacon data", info)) - } + private fun iBeaconAdv( + deviceInfo: BluetoothDeviceInfo, + rows: MutableList> + ) { + IBeaconInfo.getIBeaconInfo(deviceInfo.scanInfo?.scanRecord?.bytes!!) + ?.let { beaconInfo -> + val info = StringBuilder().apply { + append("Minor: ").append(beaconInfo.minor).append("
") + append("Major: ").append(beaconInfo.major).append("
") + append("UUID: ").append(beaconInfo.uuid).append("
") + append("RSSI at 1m: ").append(beaconInfo.power) + }.toString() + rows.add(Pair("iBeacon data", info)) + } } - private fun eddystoneAdv(deviceInfo: BluetoothDeviceInfo, rows: MutableList>) { + private fun eddystoneAdv( + deviceInfo: BluetoothDeviceInfo, + rows: MutableList> + ) { var dataValue = "" val scanInfo = deviceInfo.scanInfo val deviceAddress = scanInfo?.device?.address @@ -364,13 +435,21 @@ class DebugModeDeviceAdapter( // get url string var eddystoneUrl = beacon.urlStatus.toString() - eddystoneUrl = if (TextUtils.isEmpty(eddystoneUrl)) context.getString(R.string.beacon_details_dialog_unknown_value) else eddystoneUrl + eddystoneUrl = + if (TextUtils.isEmpty(eddystoneUrl)) context.getString(R.string.beacon_details_dialog_unknown_value) else eddystoneUrl // get uid string // Eddystone UID Advertisement Data: 16-byte Beacon ID (10-byte namespace, 6-byte instance) val eddystoneUid = beacon.uidStatus.uidValue - val eddystoneUidValue = if (TextUtils.isEmpty(eddystoneUid)) context.getString(R.string.beacon_details_dialog_unknown_value) else eddystoneUid?.substring(0, 20) - val eddystoneUidNameSpace = if (TextUtils.isEmpty(eddystoneUid)) context.getString(R.string.beacon_details_dialog_unknown_value) else "" + eddystoneUid?.substring(20) + val eddystoneUidValue = + if (TextUtils.isEmpty(eddystoneUid)) context.getString(R.string.beacon_details_dialog_unknown_value) else eddystoneUid?.substring( + 0, + 20 + ) + val eddystoneUidNameSpace = + if (TextUtils.isEmpty(eddystoneUid)) context.getString(R.string.beacon_details_dialog_unknown_value) else "" + eddystoneUid?.substring( + 20 + ) // get tlm data val version = beacon.tlmStatus.version @@ -379,18 +458,22 @@ class DebugModeDeviceAdapter( var voltageValue = 0.0 if (voltage != null) { voltageValue = voltage.toDouble() - voltage = (voltageValue / 1000).toString() + " " + context.getString(R.string.beacon_details_dialog_unit_volts) + voltage = + (voltageValue / 1000).toString() + " " + context.getString(R.string.beacon_details_dialog_unit_volts) } val temperature = beacon.tlmStatus.temp + " " + context.getString(R.string.beacon_details_dialog_unit_degrees_celsius) val advertisementCount = beacon.tlmStatus.advCnt var uptimeCountInSeconds = beacon.tlmStatus.deciSecondsCntVal / 10 uptimeCountInSeconds = Math.round(uptimeCountInSeconds * 10) / 10.0 - val secondsLabel = context.getString(R.string.beacon_details_dialog_unit_seconds_abbreviated) + val secondsLabel = + context.getString(R.string.beacon_details_dialog_unit_seconds_abbreviated) val daysLabel = context.getString(R.string.beacon_details_dialog_unit_days) - val uptimeCount = String.format("%d $secondsLabel (%d $daysLabel)", - uptimeCountInSeconds.toInt(), - TimeUnit.SECONDS.toDays(uptimeCountInSeconds.roundToLong())) + val uptimeCount = String.format( + "%d $secondsLabel (%d $daysLabel)", + uptimeCountInSeconds.toInt(), + TimeUnit.SECONDS.toDays(uptimeCountInSeconds.roundToLong()) + ) var eddystoneTlm = "" eddystoneTlm += context.getString(R.string.beacon_details_dialog_tlm_version) + ": " + version + "
" eddystoneTlm += context.getString(R.string.beacon_details_dialog_tlm_voltage) + ": " + voltage + "
" @@ -399,7 +482,8 @@ class DebugModeDeviceAdapter( eddystoneTlm += context.getString(R.string.beacon_details_dialog_tlm_advertisement_count) + ": " + advertisementCount + "
" eddystoneTlm += context.getString(R.string.beacon_details_dialog_tlm_uptime) + ": " + uptimeCount - eddystoneTlm = if (TextUtils.isEmpty(eddystoneTlm)) context.getString(R.string.beacon_details_dialog_unknown_value) else eddystoneTlm + eddystoneTlm = + if (TextUtils.isEmpty(eddystoneTlm)) context.getString(R.string.beacon_details_dialog_unknown_value) else eddystoneTlm dataValue += context.getString(R.string.beacon_details_dialog_url) + ": " + eddystoneUrl + "
" dataValue += context.getString(R.string.beacon_details_dialog_uid) + ": " + eddystoneUidValue + "
" @@ -410,7 +494,10 @@ class DebugModeDeviceAdapter( rows.add(Pair("Eddystone data", dataValue)) } - private fun altBeaconAdv(deviceInfo: BluetoothDeviceInfo, rows: MutableList>) { + private fun altBeaconAdv( + deviceInfo: BluetoothDeviceInfo, + rows: MutableList> + ) { val altBeacon = AltBeacon(deviceInfo) val beaconId = altBeacon.altBeaconId @@ -421,10 +508,14 @@ class DebugModeDeviceAdapter( append(context.getString(R.string.beacon_details_dialog_beacon_id)).append("
") append(beaconId).append("

") - append(context.getString(R.string.beacon_details_dialog_manufacturer_id)).append(mfgId) + append(context.getString(R.string.beacon_details_dialog_manufacturer_id)).append( + mfgId + ) append("

") - append(context.getString(R.string.beacon_details_dialog_reference_rssi)).append(refRssi) + append(context.getString(R.string.beacon_details_dialog_reference_rssi)).append( + refRssi + ) append(" dBm
") //non-breaking space in HTML to separate value with a unit (dBm) }.toString() diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/AboutUuidDictionaryDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/AboutUuidDictionaryDialog.kt index 788b6a54..87b05dc3 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/AboutUuidDictionaryDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/AboutUuidDictionaryDialog.kt @@ -4,22 +4,30 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import by.kirich1409.viewbindingdelegate.viewBinding import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.dialog_about_uuid_dictionary.* +import com.siliconlabs.bledemo.databinding.DialogAboutUuidDictionaryBinding + class AboutUuidDictionaryDialog : BaseDialogFragment( - hasCustomWidth = true, - isCanceledOnTouchOutside = true + hasCustomWidth = true, + isCanceledOnTouchOutside = true ) { + private lateinit var binding: DialogAboutUuidDictionaryBinding //dialog_about_uuid_dictionary - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_about_uuid_dictionary, container, false) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogAboutUuidDictionaryBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - btn_ok.setOnClickListener { dismiss() } + binding.btnOk.setOnClickListener { dismiss() } } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/ErrorDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/ErrorDialog.kt index 9315649a..514e459f 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/ErrorDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/ErrorDialog.kt @@ -9,29 +9,36 @@ import android.view.ViewGroup import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.bluetooth.ble.ErrorCodes.getATTHTMLFormattedError import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.dialog_error.* +import com.siliconlabs.bledemo.databinding.DialogErrorBinding + class ErrorDialog( - private val errorCode: Int, - private val otaErrorCallback: OtaErrorCallback + private val errorCode: Int, + private val otaErrorCallback: OtaErrorCallback ) : BaseDialogFragment( - hasCustomWidth = true, - isCanceledOnTouchOutside = false + hasCustomWidth = true, + isCanceledOnTouchOutside = false ) { - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_error, container, false) + private lateinit var binding: DialogErrorBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogErrorBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - btn_ok.setOnClickListener { + binding.btnOk.setOnClickListener { dismiss() otaErrorCallback.onDismiss() } - error_description.text = Html.fromHtml(getATTHTMLFormattedError(errorCode)) + binding.errorDescription + .text = Html.fromHtml(getATTHTMLFormattedError(errorCode)) } override fun onDismiss(dialog: DialogInterface) { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/LeaveApplicationDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/LeaveApplicationDialog.kt index 4283d22b..25db7951 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/LeaveApplicationDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/LeaveApplicationDialog.kt @@ -6,32 +6,43 @@ import android.view.View import android.view.ViewGroup import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.DialogInfoOkCancelBinding import com.siliconlabs.bledemo.utils.SharedPrefUtils -import kotlinx.android.synthetic.main.dialog_info_ok_cancel.* + class LeaveApplicationDialog(var callback: Callback) : BaseDialogFragment() { - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_info_ok_cancel, container, false) + private lateinit var binding: DialogInfoOkCancelBinding //dialog_info_ok_cancel + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogInfoOkCancelBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - tv_dialog_title.text = context?.getString(R.string.Leave_Application) - tv_dialog_content.text = context?.getString(R.string.leave_application_info) + binding.tvDialogTitle + .text = context?.getString(R.string.Leave_Application) + binding.tvDialogContent + .text = context?.getString(R.string.leave_application_info) handleClickEvents() } private fun handleClickEvents() { - btn_ok.setOnClickListener { - if (cb_dont_show_again.isChecked) SharedPrefUtils(requireContext()).setShouldLeaveApplicationDialog(false) + binding.btnOk.setOnClickListener { + if (binding.cbDontShowAgain.isChecked) SharedPrefUtils(requireContext()).setShouldLeaveApplicationDialog( + false + ) callback.onOkClicked() dismiss() } - btn_cancel.setOnClickListener { + binding.btnCancel.setOnClickListener { dismiss() } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/ManualUnbondDeviceDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/ManualUnbondDeviceDialog.kt index e534b169..5bab1285 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/ManualUnbondDeviceDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/ManualUnbondDeviceDialog.kt @@ -6,25 +6,39 @@ import android.view.View import android.view.ViewGroup import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.DialogInfoOkCancelBinding import com.siliconlabs.bledemo.utils.SharedPrefUtils -import kotlinx.android.synthetic.main.dialog_info_ok_cancel.view.* + class ManualUnbondDeviceDialog(val callback: Callback) : BaseDialogFragment() { + private lateinit var binding: DialogInfoOkCancelBinding//dialog_info_ok_cancel + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogInfoOkCancelBinding.inflate(inflater, container, false).apply { - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_info_ok_cancel, container, false).apply { + binding.tvDialogTitle.text = + requireContext().getString(R.string.device_services_title_unbond_device_manual) + binding.tvDialogContent.text = + requireContext().getString(R.string.device_services_note_unbond_device_manual) - tv_dialog_title.text = context.getString(R.string.device_services_title_unbond_device_manual) - tv_dialog_content.text = context.getString(R.string.device_services_note_unbond_device_manual) - btn_ok.text = context.getString(R.string.button_proceed) + binding.btnOk.text = requireContext().getString(R.string.button_proceed) - btn_ok.setOnClickListener { - if (cb_dont_show_again.isChecked) SharedPrefUtils(requireContext()).setShouldDisplayManualUnbondDeviceDialog(false) + binding.btnOk.setOnClickListener { + if (binding.cbDontShowAgain + .isChecked) SharedPrefUtils(requireContext()).setShouldDisplayManualUnbondDeviceDialog( + false + ) callback.onOkClicked() dismiss() } - btn_cancel.setOnClickListener { dismiss() } + binding.btnCancel.setOnClickListener { dismiss() } } + + return binding.root } interface Callback { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/OtaCharacteristicMissingDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/OtaCharacteristicMissingDialog.kt index 82806fd7..03a0bf2b 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/OtaCharacteristicMissingDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/OtaCharacteristicMissingDialog.kt @@ -6,21 +6,26 @@ import android.view.View import android.view.ViewGroup import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R -import kotlinx.android.synthetic.main.dialog_ota_missing_characteristic.* +import com.siliconlabs.bledemo.databinding.DialogOtaMissingCharacteristicBinding + class OtaCharacteristicMissingDialog : BaseDialogFragment( - hasCustomWidth = true, - isCanceledOnTouchOutside = true + hasCustomWidth = true, + isCanceledOnTouchOutside = true ) { - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_ota_missing_characteristic, null) + private lateinit var binding: DialogOtaMissingCharacteristicBinding//dialog_ota_missing_characteristic + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogOtaMissingCharacteristicBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - button_ok.setOnClickListener { dismiss() } + binding.buttonOk.setOnClickListener { dismiss() } } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/UnbondDeviceDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/UnbondDeviceDialog.kt index f96e8a14..06e78cce 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/UnbondDeviceDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/dialogs/UnbondDeviceDialog.kt @@ -6,24 +6,37 @@ import android.view.View import android.view.ViewGroup import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.DialogInfoOkCancelBinding import com.siliconlabs.bledemo.utils.SharedPrefUtils -import kotlinx.android.synthetic.main.dialog_info_ok_cancel.view.* + class UnbondDeviceDialog(val callback: Callback) : BaseDialogFragment() { - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_info_ok_cancel, container, false).apply { + private lateinit var binding: DialogInfoOkCancelBinding//dialog_info_ok_cancel + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogInfoOkCancelBinding.inflate(inflater, container, false).apply { - tv_dialog_title.text = context.getString(R.string.device_services_title_unbond_device) - tv_dialog_content.text = context.getString(R.string.device_services_note_unbond_device) + tvDialogTitle.text = + requireContext().getString(R.string.device_services_title_unbond_device) + tvDialogContent.text = + requireContext().getString(R.string.device_services_note_unbond_device) - btn_ok.setOnClickListener { - if (cb_dont_show_again.isChecked) SharedPrefUtils(requireContext()).setShouldDisplayUnbondDeviceDialog(false) - callback.onOkClicked() - dismiss() - } - btn_cancel.setOnClickListener { dismiss() } + btnOk + .setOnClickListener { + if (cbDontShowAgain.isChecked) SharedPrefUtils(requireContext()).setShouldDisplayUnbondDeviceDialog( + false + ) + callback.onOkClicked() + dismiss() + } + btnCancel.setOnClickListener { dismiss() } } + return binding.root } interface Callback { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/BrowserFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/BrowserFragment.kt index addf5977..11dc96ef 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/BrowserFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/BrowserFragment.kt @@ -52,7 +52,8 @@ import com.siliconlabs.bledemo.home_screen.viewmodels.ScanFragmentViewModel import com.siliconlabs.bledemo.features.scan.browser.adapters.DebugModeCallback import com.siliconlabs.bledemo.home_screen.base.BluetoothDependent import com.siliconlabs.bledemo.home_screen.base.LocationDependent -import kotlinx.android.synthetic.main.fragment_browser.view.* +import com.siliconlabs.bledemo.home_screen.base.NotificationDependent + class BrowserFragment : BaseServiceDependentMainMenuFragment(), OnRefreshListener { @@ -126,6 +127,7 @@ class BrowserFragment : BaseServiceDependentMainMenuFragment(), } } + private val scanFragmentListener = object: ScanFragment.ScanFragmentListener{ override fun onScanningStateChanged(isOn: Boolean) { toggleMainView(isOn, viewModel.isAnyDeviceDiscovered.value ?: false) diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/CharacteristicMappingsFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/CharacteristicMappingsFragment.kt index 516f90f9..d16e5983 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/CharacteristicMappingsFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/CharacteristicMappingsFragment.kt @@ -10,8 +10,9 @@ import com.siliconlabs.bledemo.features.scan.browser.adapters.DictionaryEntryAda import com.siliconlabs.bledemo.features.scan.browser.models.Mapping import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.common.other.CardViewListDecoration +import com.siliconlabs.bledemo.databinding.FragmentCharacteristicMappingsBinding import com.siliconlabs.bledemo.utils.SharedPrefUtils -import kotlinx.android.synthetic.main.fragment_characteristic_mappings.* + import java.util.* import kotlin.collections.ArrayList @@ -19,6 +20,7 @@ class CharacteristicMappingsFragment : Fragment() { private lateinit var map: HashMap private lateinit var list: ArrayList private lateinit var sharedPrefUtils: SharedPrefUtils + private lateinit var binding: FragmentCharacteristicMappingsBinding //fragment_characteristic_mappings override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -27,16 +29,22 @@ class CharacteristicMappingsFragment : Fragment() { list = ArrayList(map.values) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_characteristic_mappings, container, false) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentCharacteristicMappingsBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - rv_characteristics.apply { + binding.rvCharacteristics.apply { layoutManager = LinearLayoutManager(activity) addItemDecoration(CardViewListDecoration()) - adapter = DictionaryEntryAdapter(list, requireContext(), Mapping.Type.CHARACTERISTIC) + adapter = + DictionaryEntryAdapter(list, requireContext(), Mapping.Type.CHARACTERISTIC) } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/FilterFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/FilterFragment.kt index dc841ee2..9d6409a6 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/FilterFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/FilterFragment.kt @@ -11,7 +11,7 @@ import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.databinding.FragmentFilterBinding import com.siliconlabs.bledemo.home_screen.fragments.ScanFragment import com.siliconlabs.bledemo.utils.FilterDeviceParams -import kotlinx.android.synthetic.main.fragment_filter.* +//import kotlinx.android.synthetic.main.fragment_filter.* import java.util.* class FilterFragment : DialogFragment() { @@ -25,7 +25,11 @@ class FilterFragment : DialogFragment() { viewModel = (parentFragment as ScanFragment).viewModel } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { setHasOptionsMenu(true) viewBinding = FragmentFilterBinding.inflate(inflater) return viewBinding.root @@ -50,6 +54,7 @@ class FilterFragment : DialogFragment() { resetFilters() true } + else -> super.onOptionsItemSelected(item) } } @@ -72,10 +77,13 @@ class FilterFragment : DialogFragment() { seekBarRssi.seekControlBar.progress = getSeekbarProgress(it.rssiValue) seekBarRssi.seekControlText.text = getString(R.string.n_dBm, it.rssiValue) } - if (it.bleFormats.contains(BleFormat.UNSPECIFIED)) beaconTypeUnspecified.isChecked = true - if (it.bleFormats.contains(BleFormat.ALT_BEACON)) beaconTypeAltBeacon.isChecked = true + if (it.bleFormats.contains(BleFormat.UNSPECIFIED)) beaconTypeUnspecified.isChecked = + true + if (it.bleFormats.contains(BleFormat.ALT_BEACON)) beaconTypeAltBeacon.isChecked = + true if (it.bleFormats.contains(BleFormat.I_BEACON)) beaconTypeIBeacon.isChecked = true - if (it.bleFormats.contains(BleFormat.EDDYSTONE)) beaconTypeEddystone.isChecked = true + if (it.bleFormats.contains(BleFormat.EDDYSTONE)) beaconTypeEddystone.isChecked = + true if (it.isOnlyConnectable) cbOnlyConnectable.isChecked = true if (it.isOnlyBonded) cbOnlyBonded.isChecked = true @@ -117,7 +125,8 @@ class FilterFragment : DialogFragment() { } } - seekBarRssi.seekControlBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { + seekBarRssi.seekControlBar.setOnSeekBarChangeListener(object : + SeekBar.OnSeekBarChangeListener { override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { rssiFlag = true val rssi = getRssiValue(progress) @@ -134,7 +143,7 @@ class FilterFragment : DialogFragment() { return progress - requireContext().resources.getInteger(R.integer.rssi_value_range) } - private fun getSeekbarProgress(rssiValue: Int) : Int { + private fun getSeekbarProgress(rssiValue: Int): Int { return rssiValue + requireContext().resources.getInteger(R.integer.rssi_value_range) } @@ -154,13 +163,13 @@ class FilterFragment : DialogFragment() { val name = viewBinding.etSearchDeviceName.text.toString() val rssi = getRssiValue(viewBinding.seekBarRssi.seekControlBar.progress) val activeFilter = FilterDeviceParams( - if (name.isNotBlank()) name else null, - rssi, - rssiFlag, - selectedBeacons, - cb_only_favourites.isChecked, - cb_only_connectable.isChecked, - cb_only_bonded.isChecked + if (name.isNotBlank()) name else null, + rssi, + rssiFlag, + selectedBeacons, + viewBinding.cbOnlyFavourites.isChecked, + viewBinding.cbOnlyConnectable.isChecked, + viewBinding.cbOnlyBonded.isChecked ) return if (!activeFilter.isEmpty) activeFilter else null } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/RemoteServicesFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/RemoteServicesFragment.kt index 2da76457..869b30e1 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/RemoteServicesFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/RemoteServicesFragment.kt @@ -11,10 +11,9 @@ import androidx.core.content.ContextCompat import androidx.core.widget.ImageViewCompat import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.databinding.PropertyContainerBinding -import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.Property import com.siliconlabs.bledemo.utils.BLEUtils import com.siliconlabs.bledemo.utils.Notifications -import kotlinx.android.synthetic.main.fragment_services.view.* + @SuppressLint("MissingPermission") class RemoteServicesFragment(private val onScrollChangeListener: View.OnScrollChangeListener) : ServicesFragment(isRemote = true) { @@ -23,7 +22,8 @@ class RemoteServicesFragment(private val onScrollChangeListener: View.OnScrollCh override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.swipeRefreshContainer.scrollview.setOnScrollChangeListener(onScrollChangeListener) + // binding.swipeRefreshContainer.scrollview.setOnScrollChangeListener(onScrollChangeListener) + binding.swipeRefreshContainer.setOnScrollChangeListener(onScrollChangeListener) } override fun readCharacteristic(bluetoothGattCharacteristic: BluetoothGattCharacteristic) { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/ServiceMappingsFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/ServiceMappingsFragment.kt index a8eccd78..b90fd98a 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/ServiceMappingsFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/ServiceMappingsFragment.kt @@ -10,8 +10,9 @@ import com.siliconlabs.bledemo.features.scan.browser.adapters.DictionaryEntryAda import com.siliconlabs.bledemo.features.scan.browser.models.Mapping import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.common.other.CardViewListDecoration +import com.siliconlabs.bledemo.databinding.FragmentServiceMappingsBinding +import com.siliconlabs.bledemo.databinding.FragmentServicesBinding import com.siliconlabs.bledemo.utils.SharedPrefUtils -import kotlinx.android.synthetic.main.fragment_service_mappings.* import java.util.* import kotlin.collections.ArrayList @@ -19,6 +20,7 @@ class ServiceMappingsFragment : Fragment() { private lateinit var map: HashMap private lateinit var list: ArrayList private lateinit var sharedPrefUtils: SharedPrefUtils + private lateinit var binding: FragmentServiceMappingsBinding //fragment_service_mappings override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -27,13 +29,18 @@ class ServiceMappingsFragment : Fragment() { list = ArrayList(map.values) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_service_mappings, container, false) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentServiceMappingsBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - rv_services.apply { + binding.rvServices.apply { layoutManager = LinearLayoutManager(activity) addItemDecoration(CardViewListDecoration()) adapter = DictionaryEntryAdapter(list, requireContext(), Mapping.Type.SERVICE) diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/ServicesFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/ServicesFragment.kt index 47d571a1..added969 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/ServicesFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/browser/fragments/ServicesFragment.kt @@ -10,23 +10,23 @@ import android.view.ViewGroup import android.widget.LinearLayout import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment +import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.bluetooth.parsing.DescriptorParser +import com.siliconlabs.bledemo.databinding.FragmentServicesBinding +import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.Property +import com.siliconlabs.bledemo.features.iop_test.models.CommonUUID import com.siliconlabs.bledemo.features.scan.browser.activities.DeviceServicesActivity -import com.siliconlabs.bledemo.features.scan.browser.dialogs.DictionaryEntryEditDialog import com.siliconlabs.bledemo.features.scan.browser.adapters.MappingCallback +import com.siliconlabs.bledemo.features.scan.browser.dialogs.DictionaryEntryEditDialog import com.siliconlabs.bledemo.features.scan.browser.models.Mapping -import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.features.scan.browser.views.CharacteristicItemContainer import com.siliconlabs.bledemo.features.scan.browser.views.DescriptorContainer import com.siliconlabs.bledemo.features.scan.browser.views.ServiceItemContainer -import com.siliconlabs.bledemo.databinding.FragmentServicesBinding -import com.siliconlabs.bledemo.features.configure.gatt_configurator.models.Property -import com.siliconlabs.bledemo.features.iop_test.models.CommonUUID import com.siliconlabs.bledemo.utils.* -import kotlinx.android.synthetic.main.descriptor_container.view.* import java.util.* -abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layout.fragment_services) { +abstract class ServicesFragment(private val isRemote: Boolean) : + Fragment(R.layout.fragment_services) { protected lateinit var binding: FragmentServicesBinding @@ -40,9 +40,11 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo protected var currentWriteReadFragment: FragmentCharacteristicDetail? = null private val descriptorsMap = mutableMapOf() - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - binding = FragmentServicesBinding.inflate(inflater) + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentServicesBinding.inflate(inflater, container, false) return binding.root } @@ -62,10 +64,10 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo (activity as DeviceServicesActivity).refreshServices() } setColorSchemeColors( - ContextCompat.getColor(requireContext(), android.R.color.holo_red_dark), - ContextCompat.getColor(requireContext(), android.R.color.holo_orange_dark), - ContextCompat.getColor(requireContext(), android.R.color.holo_orange_light), - ContextCompat.getColor(requireContext(), android.R.color.holo_red_light) + ContextCompat.getColor(requireContext(), android.R.color.holo_red_dark), + ContextCompat.getColor(requireContext(), android.R.color.holo_orange_dark), + ContextCompat.getColor(requireContext(), android.R.color.holo_orange_light), + ContextCompat.getColor(requireContext(), android.R.color.holo_red_light) ) } } @@ -82,11 +84,20 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo binding.servicesContainer.removeAllViews() } +// fun updateDescriptorView(descriptor: BluetoothGattDescriptor) { +// descriptorsMap[descriptor]?.apply { +// activity?.runOnUiThread { +// +// container_descriptor_value.visibility = View.VISIBLE +// tv_value.text = DescriptorParser(descriptor).getFormattedValue() +// } +// } +// } + //TODO fun updateDescriptorView(descriptor: BluetoothGattDescriptor) { descriptorsMap[descriptor]?.apply { activity?.runOnUiThread { - container_descriptor_value.visibility = View.VISIBLE - tv_value.text = DescriptorParser(descriptor).getFormattedValue() + } } } @@ -125,20 +136,23 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo private val characteristicContainerCallback = object : CharacteristicItemContainer.Callback { override fun onRenameClicked(container: CharacteristicItemContainer) { - DictionaryEntryEditDialog( - container.getCharacteristicName(), - UuidUtils.getUuidText(container.characteristic.uuid), - Mapping.Type.CHARACTERISTIC, - object : MappingCallback { - override fun onNameChanged(mapping: Mapping) { - container.setCharacteristicName(mapping.name) - characteristicNamesMap[mapping.uuid] = mapping - } - } - ).show(parentFragmentManager, "dialog_mapping_edit") + DictionaryEntryEditDialog( + container.getCharacteristicName(), + UuidUtils.getUuidText(container.characteristic.uuid), + Mapping.Type.CHARACTERISTIC, + object : MappingCallback { + override fun onNameChanged(mapping: Mapping) { + container.setCharacteristicName(mapping.name) + characteristicNamesMap[mapping.uuid] = mapping + } + } + ).show(parentFragmentManager, "dialog_mapping_edit") } - override fun onPropertyClicked(property: Property, characteristicContainer: CharacteristicItemContainer) { + override fun onPropertyClicked( + property: Property, + characteristicContainer: CharacteristicItemContainer + ) { handleOnPropertyClicked(property, characteristicContainer) } } @@ -153,29 +167,29 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo context?.let { services.forEachIndexed { position, service -> val serviceItemContainer = ServiceItemContainer( - it, - serviceContainerCallback, - service, - isMandatorySystemService(service.uuid), - serviceNamesMap + it, + serviceContainerCallback, + service, + isMandatorySystemService(service.uuid), + serviceNamesMap ) service.characteristics.forEach { char -> val characteristicContainer = CharacteristicItemContainer( - it, - characteristicContainerCallback, - char, - isMandatorySystemService(service.uuid), - characteristicNamesMap + it, + characteristicContainerCallback, + char, + isMandatorySystemService(service.uuid), + characteristicNamesMap ) if (char.descriptors.isEmpty()) characteristicContainer.hideDescriptorsContainer() else { char.descriptors.forEach { descriptor -> DescriptorContainer( - it, - descriptorContainerCallback, - descriptor + it, + descriptorContainerCallback, + descriptor ).also { characteristicContainer.addDescriptorContainer(it) descriptorsMap[descriptor] = it @@ -187,23 +201,24 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo serviceItemContainer.setMargins(position) binding.servicesContainer.addView( - serviceItemContainer, - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT + serviceItemContainer, + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT ) } - (activity as? DeviceServicesActivity)?.isUiCreated = true // prevent from crashes when clicking "back" when still loading + (activity as? DeviceServicesActivity)?.isUiCreated = + true // prevent from crashes when clicking "back" when still loading } } - private fun initFragmentCharacteristicDetail( - bluetoothGattCharacteristic: BluetoothGattCharacteristic, - expansionId: Int, - service: BluetoothGattService, - characteristicExpansion: LinearLayout, - displayWriteDialog: Boolean = false, - writeType: FragmentCharacteristicDetail.WriteType = FragmentCharacteristicDetail.WriteType.REMOTE_WRITE + private fun initFragmentCharacteristicDetail( + bluetoothGattCharacteristic: BluetoothGattCharacteristic, + expansionId: Int, + service: BluetoothGattService, + characteristicExpansion: LinearLayout, + displayWriteDialog: Boolean = false, + writeType: FragmentCharacteristicDetail.WriteType = FragmentCharacteristicDetail.WriteType.REMOTE_WRITE ): FragmentCharacteristicDetail { val characteristicDetail = FragmentCharacteristicDetail().apply { isRemote = this@ServicesFragment.isRemote @@ -215,17 +230,17 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo } // show characteristic's expansion and add the fragment to view/edit characteristic detail parentFragmentManager - .beginTransaction() - .add(expansionId, characteristicDetail, CHARACTERISTIC_ADD_FRAGMENT_TRANSACTION_ID) - .commitNow() + .beginTransaction() + .add(expansionId, characteristicDetail, CHARACTERISTIC_ADD_FRAGMENT_TRANSACTION_ID) + .commitNow() return characteristicDetail } private fun handleOnPropertyClicked( - property: Property, - characteristicContainer: CharacteristicItemContainer + property: Property, + characteristicContainer: CharacteristicItemContainer ) { if (isMandatorySystemService(characteristicContainer.characteristic.service.uuid)) return @@ -236,16 +251,17 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo when (property) { Property.READ -> { saveCurrentWriteReadFragment( - bluetoothGattCharacteristic, - service, - characteristicExpansion + bluetoothGattCharacteristic, + service, + characteristicExpansion ) readCharacteristic(bluetoothGattCharacteristic) } + Property.WRITE -> { val writeType = - if (this is RemoteServicesFragment) FragmentCharacteristicDetail.WriteType.REMOTE_WRITE - else FragmentCharacteristicDetail.WriteType.LOCAL_WRITE + if (this is RemoteServicesFragment) FragmentCharacteristicDetail.WriteType.REMOTE_WRITE + else FragmentCharacteristicDetail.WriteType.LOCAL_WRITE openWriteDialog( bluetoothGattCharacteristic, service, @@ -253,20 +269,20 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo writeType ) } + Property.NOTIFY -> { if (this is RemoteServicesFragment) { saveCurrentWriteReadFragment( - bluetoothGattCharacteristic, - service, - characteristicExpansion + bluetoothGattCharacteristic, + service, + characteristicExpansion ) toggleNotifications( bluetoothGattCharacteristic, characteristicContainer.getPropertyBinding(Property.NOTIFY), characteristicContainer.getPropertyBinding(Property.INDICATE) ) - } - else { + } else { openWriteDialog( bluetoothGattCharacteristic, service, @@ -275,20 +291,20 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo ) } } + Property.INDICATE -> { if (this is RemoteServicesFragment) { saveCurrentWriteReadFragment( - bluetoothGattCharacteristic, - service, - characteristicExpansion + bluetoothGattCharacteristic, + service, + characteristicExpansion ) toggleIndications( bluetoothGattCharacteristic, characteristicContainer.getPropertyBinding(Property.INDICATE), characteristicContainer.getPropertyBinding(Property.NOTIFY) ) - } - else { + } else { openWriteDialog( bluetoothGattCharacteristic, service, @@ -297,7 +313,8 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo ) } } - else -> { } + + else -> {} } } @@ -327,9 +344,9 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo } private fun saveCurrentWriteReadFragment( - characteristic: BluetoothGattCharacteristic, - service: BluetoothGattService, - characteristicExpansion: LinearLayout + characteristic: BluetoothGattCharacteristic, + service: BluetoothGattService, + characteristicExpansion: LinearLayout ) { val id = characteristicExpansion.id @@ -337,10 +354,10 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo currentWriteReadFragment = characteristicFragments[id] } else { currentWriteReadFragment = initFragmentCharacteristicDetail( - characteristic, - id, - service, - characteristicExpansion + characteristic, + id, + service, + characteristicExpansion ) characteristicFragments[id] = currentWriteReadFragment } @@ -353,7 +370,7 @@ abstract class ServicesFragment(private val isRemote: Boolean) : Fragment(R.layo private fun isMandatorySystemService(uuid: UUID): Boolean { return (!isRemote && (uuid == UUID.fromString(CommonUUID.Service.UUID_GENERIC_ACCESS.toString()) - || uuid == UUID.fromString(CommonUUID.Service.UUID_GENERIC_ATTRIBUTE.toString()))) + || uuid == UUID.fromString(CommonUUID.Service.UUID_GENERIC_ATTRIBUTE.toString()))) } override fun onPause() { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/rssi_graph/fragments/RssiGraphFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/rssi_graph/fragments/RssiGraphFragment.kt index 2d1ee557..1a975ad8 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/rssi_graph/fragments/RssiGraphFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/scan/rssi_graph/fragments/RssiGraphFragment.kt @@ -25,6 +25,7 @@ import com.siliconlabs.bledemo.features.scan.rssi_graph.utils.GraphDataExporter import com.siliconlabs.bledemo.features.scan.rssi_graph.views.ChartView import com.siliconlabs.bledemo.home_screen.base.BluetoothDependent import com.siliconlabs.bledemo.home_screen.base.LocationDependent +import com.siliconlabs.bledemo.home_screen.base.NotificationDependent import java.text.DateFormat import java.util.* import java.util.concurrent.ScheduledThreadPoolExecutor diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/activities/MainActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/activities/MainActivity.kt index d04e208a..3023cb89 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/activities/MainActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/activities/MainActivity.kt @@ -1,6 +1,7 @@ package com.siliconlabs.bledemo.home_screen.activities import android.Manifest +import android.app.NotificationManager import android.content.pm.PackageManager import android.os.Build import android.os.Bundle @@ -13,25 +14,26 @@ import androidx.navigation.ui.NavigationUI import com.siliconlabs.bledemo.base.activities.BaseActivity import com.siliconlabs.bledemo.bluetooth.services.BluetoothService import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.ActivityMainBinding import com.siliconlabs.bledemo.home_screen.dialogs.PermissionsDialog import com.siliconlabs.bledemo.home_screen.viewmodels.MainActivityViewModel import com.siliconlabs.bledemo.home_screen.views.HidableBottomNavigationView import dagger.hilt.android.AndroidEntryPoint -import kotlinx.android.synthetic.main.activity_main.* + @AndroidEntryPoint open class MainActivity : BaseActivity(), BluetoothService.ServicesStateListener { - + private lateinit var _binding:ActivityMainBinding private lateinit var viewModel: MainActivityViewModel private lateinit var binding: BluetoothService.Binding var bluetoothService: BluetoothService? = null private set private val neededPermissions = mutableListOf( - Manifest.permission.ACCESS_FINE_LOCATION - ) + Manifest.permission.ACCESS_FINE_LOCATION, + ) @RequiresApi(Build.VERSION_CODES.S) private val android12Permissions = listOf( @@ -44,7 +46,9 @@ open class MainActivity : BaseActivity(), setTheme(R.style.MainAppTheme) super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + _binding = ActivityMainBinding.inflate(LayoutInflater.from(this)) + // setContentView(R.layout.activity_main) + setContentView(_binding.root) supportActionBar?.show() viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java) @@ -66,7 +70,8 @@ open class MainActivity : BaseActivity(), private fun setupMainNavigationListener() { val navFragment = supportFragmentManager.findFragmentById(R.id.main_fragment) as NavHostFragment val navController = navFragment.navController - NavigationUI.setupWithNavController(main_navigation, navController) + + NavigationUI.setupWithNavController(_binding.mainNavigation, navController) } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -81,9 +86,9 @@ open class MainActivity : BaseActivity(), fun toggleMainNavigation(isOn: Boolean) { if(isOn) { - main_navigation.show(instant = true) + _binding.mainNavigation.show(instant = true) } else { - main_navigation.hide(instant = true) + _binding.mainNavigation.hide(instant = true) } } @@ -114,6 +119,7 @@ open class MainActivity : BaseActivity(), bluetoothService?.let { viewModel.setIsBluetoothOn(it.isBluetoothOn()) viewModel.setIsLocationOn(it.isLocationOn()) + viewModel.setIsNotificationOn(it.areNotificationOn()) } observeChanges() viewModel.setIsSetupFinished(isSetupFinished = true) @@ -127,7 +133,7 @@ open class MainActivity : BaseActivity(), } fun getMainNavigation(): HidableBottomNavigationView? { - return main_navigation + return _binding.mainNavigation } override fun onBluetoothStateChanged(isOn: Boolean) { @@ -138,6 +144,9 @@ open class MainActivity : BaseActivity(), viewModel.setIsLocationOn(isOn) } + override fun onNotificationStateChanged(isOn: Boolean) { + viewModel.setIsNotificationOn(isOn) + } private fun handlePermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/BaseMainMenuFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/BaseMainMenuFragment.kt index a358c258..a6b4b08a 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/BaseMainMenuFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/BaseMainMenuFragment.kt @@ -22,7 +22,7 @@ abstract class BaseMainMenuFragment : Fragment(), WithHidableUIElements { } } - private fun restoreHiddenUI() { + fun restoreHiddenUI() { hidableActionButton?.show() bottomNav.show() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/BaseServiceDependentMainMenuFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/BaseServiceDependentMainMenuFragment.kt index 02b42f11..5e0493f4 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/BaseServiceDependentMainMenuFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/BaseServiceDependentMainMenuFragment.kt @@ -17,6 +17,7 @@ abstract class BaseServiceDependentMainMenuFragment : BaseMainMenuFragment() { protected open val bluetoothDependent: BluetoothDependent? = null protected open val locationDependent: LocationDependent? = null protected var activityViewModel: MainActivityViewModel? = null + protected open val notificationDependent:NotificationDependent? = null protected fun toggleBluetoothBar(isOn: Boolean, bar: BluetoothEnableBar) { bar.visibility = if (isOn) View.GONE else View.VISIBLE @@ -35,6 +36,8 @@ abstract class BaseServiceDependentMainMenuFragment : BaseMainMenuFragment() { bar.visibility = if (isPermissionGranted) View.GONE else View.VISIBLE } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) activity?.let { @@ -63,6 +66,12 @@ abstract class BaseServiceDependentMainMenuFragment : BaseMainMenuFragment() { activityViewModel?.isLocationPermissionGranted?.observe(viewLifecycleOwner, Observer { isGranted -> locationDependent?.onLocationPermissionStateChanged(isGranted) }) + activityViewModel?.isNotificationOn?.observe(viewLifecycleOwner, Observer { isOn -> + notificationDependent?.onNotificationStateChanged(isOn) + }) + activityViewModel?.isNotificationPermissionGranted?.observe(viewLifecycleOwner, Observer { areGranted -> + notificationDependent?.onNotificationPermissionsStateChanged(areGranted) + }) } } @@ -74,6 +83,10 @@ abstract class BaseServiceDependentMainMenuFragment : BaseMainMenuFragment() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { bluetoothDependent?.setupBluetoothPermissionsBarButtons() } + notificationDependent?.let { + it.setupNotificationBarButtons() + it.setupNotificationPermissionBarButtons() + } } fun showToastLengthShort(message: String) { diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/NotificationDependent.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/NotificationDependent.kt new file mode 100644 index 00000000..07efb12c --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/NotificationDependent.kt @@ -0,0 +1,8 @@ +package com.siliconlabs.bledemo.home_screen.base + +interface NotificationDependent { + fun onNotificationStateChanged(isNotificationOn: Boolean) + fun onNotificationPermissionsStateChanged(arePermissionsGranted: Boolean) + fun setupNotificationBarButtons() + fun setupNotificationPermissionBarButtons() +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/ViewPagerFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/ViewPagerFragment.kt index fc3da919..8e93d0cc 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/ViewPagerFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/base/ViewPagerFragment.kt @@ -13,23 +13,28 @@ import com.siliconlabs.bledemo.features.scan.browser.fragments.BrowserFragment import com.siliconlabs.bledemo.features.scan.active_connections.fragments.ConnectionsFragment import com.siliconlabs.bledemo.home_screen.viewmodels.ScanFragmentViewModel import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.FragmentViewPagerBinding import com.siliconlabs.bledemo.home_screen.fragments.ScanFragment import com.siliconlabs.bledemo.features.scan.rssi_graph.fragments.RssiGraphFragment -import kotlinx.android.synthetic.main.fragment_view_pager.* + class ViewPagerFragment : Fragment() { private lateinit var viewModel: ScanFragmentViewModel private var bluetoothService: BluetoothService? = null + private lateinit var binding: FragmentViewPagerBinding //fragment_view_pager override fun onAttach(context: Context) { super.onAttach(context) viewModel = getScanFragment().viewModel } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_view_pager, null) + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentViewPagerBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -46,11 +51,13 @@ class ViewPagerFragment : Fragment() { } private fun initScanPager() { - scan_view_pager2.apply { + binding.scanViewPager2 + .apply { adapter = ScanPagerAdapter() } - TabLayoutMediator(scan_tab_layout, scan_view_pager2) { tab, position -> + + TabLayoutMediator(binding.scanTabLayout, binding.scanViewPager2) { tab, position -> tab.text = when (position) { 0 -> getString(R.string.tab_scanner_label) 1 -> getString(R.string.tab_rssi_graph_label) @@ -62,7 +69,7 @@ class ViewPagerFragment : Fragment() { private fun setupDataObservers() { viewModel.numberOfConnectedDevices.observe(viewLifecycleOwner) { - scan_tab_layout.getTabAt(2)?.text = getString(R.string.tab_connections_label, it) + binding.scanTabLayout.getTabAt(2)?.text = getString(R.string.tab_connections_label, it) } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/dialogs/PermissionsDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/dialogs/PermissionsDialog.kt index 44a2cdb4..3f7ab00b 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/dialogs/PermissionsDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/dialogs/PermissionsDialog.kt @@ -48,6 +48,10 @@ class PermissionsDialog( } if (rationalesToShow.contains(Manifest.permission.ACCESS_FINE_LOCATION)) { append(getString(R.string.permissions_rationale_location_html)) + append("
") + } + if(rationalesToShow.contains(Manifest.permission.POST_NOTIFICATIONS)){ + append(getString(R.string.permissions_rationale_notification_html)) } }.toString() } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/dialogs/WarningBarInfoDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/dialogs/WarningBarInfoDialog.kt index bf923d22..1dd9b11a 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/dialogs/WarningBarInfoDialog.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/dialogs/WarningBarInfoDialog.kt @@ -8,7 +8,7 @@ import by.kirich1409.viewbindingdelegate.viewBinding import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment import com.siliconlabs.bledemo.R import com.siliconlabs.bledemo.databinding.DialogLocationInfoBinding -import kotlinx.android.synthetic.main.dialog_location_info.* + class WarningBarInfoDialog(private val type: Type) : BaseDialogFragment( hasCustomWidth = true, @@ -24,7 +24,7 @@ class WarningBarInfoDialog(private val type: Type) : BaseDialogFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - btn_ok.setOnClickListener { dismiss() } + binding.btnOk .setOnClickListener { dismiss() } initTexts() } @@ -33,18 +33,21 @@ class WarningBarInfoDialog(private val type: Type) : BaseDialogFragment( Type.LOCATION -> R.string.Location_service Type.LOCATION_PERMISSION -> R.string.location_permission Type.BLUETOOTH_PERMISSIONS -> R.string.bluetooth_permissions + Type.NOTIFICATION_PERMISSION -> R.string.notification_permissions }) binding.infoDialogDescription.text = getString( when(type) { Type.LOCATION -> R.string.location_service_info Type.LOCATION_PERMISSION -> R.string.location_permission_info Type.BLUETOOTH_PERMISSIONS -> R.string.bluetooth_permissions_info + Type.NOTIFICATION_PERMISSION -> R.string.notification_permissions_info }) } enum class Type { LOCATION, LOCATION_PERMISSION, - BLUETOOTH_PERMISSIONS + BLUETOOTH_PERMISSIONS, + NOTIFICATION_PERMISSION } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/ConfigureFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/ConfigureFragment.kt index 1669debf..bb56c1c9 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/ConfigureFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/ConfigureFragment.kt @@ -5,17 +5,23 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentContainer import androidx.viewpager2.adapter.FragmentStateAdapter +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.tabs.TabLayoutMediator import com.siliconlabs.bledemo.features.configure.advertiser.fragments.AdvertiserFragment import com.siliconlabs.bledemo.R +import com.siliconlabs.bledemo.databinding.FragmentConfigureBinding import com.siliconlabs.bledemo.features.configure.gatt_configurator.fragments.GattConfiguratorFragment -import kotlinx.android.synthetic.main.fragment_configure.* + class ConfigureFragment : Fragment() { - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View { + private val binding by viewBinding(FragmentConfigureBinding::bind) + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { return inflater.inflate(R.layout.fragment_configure, null) } @@ -23,8 +29,9 @@ class ConfigureFragment : Fragment() { super.onViewCreated(view, savedInstanceState) activity?.title = getString(R.string.main_navigation_configure_title) - configure_view_pager2.adapter = ConfigurePagerAdapter() - TabLayoutMediator(configure_tab_layout, configure_view_pager2) { tab, position -> + binding.configureViewPager2 .adapter = ConfigurePagerAdapter() + + TabLayoutMediator( binding.configureTabLayout, binding.configureViewPager2) { tab, position -> tab.text = when (position) { 0 -> getString(R.string.tab_advertiser_label) 1 -> getString(R.string.tab_gatt_configurator_label) diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/DemoFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/DemoFragment.kt index 143245be..697c3932 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/DemoFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/DemoFragment.kt @@ -26,10 +26,12 @@ import com.siliconlabs.bledemo.features.demo.wifi_ota_update.AlertErrorDialog import com.siliconlabs.bledemo.features.demo.wifi_ota_update.WiFiOtaFileManager import com.siliconlabs.bledemo.features.demo.wifi_ota_update.WiFiOtaFileSelectionDialog import com.siliconlabs.bledemo.features.demo.wifi_ota_update.WiFiOtaProgressDialog +import com.siliconlabs.bledemo.features.demo.wifi_throughput.activities.WifiThroughputActivity import com.siliconlabs.bledemo.home_screen.adapters.DemoAdapter import com.siliconlabs.bledemo.home_screen.base.BaseServiceDependentMainMenuFragment import com.siliconlabs.bledemo.home_screen.base.BluetoothDependent import com.siliconlabs.bledemo.home_screen.base.LocationDependent +import com.siliconlabs.bledemo.home_screen.base.NotificationDependent import com.siliconlabs.bledemo.home_screen.dialogs.SelectDeviceDialog import com.siliconlabs.bledemo.home_screen.menu_items.Blinky import com.siliconlabs.bledemo.home_screen.menu_items.ConnectedLighting @@ -43,6 +45,7 @@ import com.siliconlabs.bledemo.home_screen.menu_items.Motion import com.siliconlabs.bledemo.home_screen.menu_items.OTADemo import com.siliconlabs.bledemo.home_screen.menu_items.RangeTest import com.siliconlabs.bledemo.home_screen.menu_items.Throughput +import com.siliconlabs.bledemo.home_screen.menu_items.WiFiThroughput import com.siliconlabs.bledemo.home_screen.menu_items.WifiCommissioning import java.io.File import java.io.FileInputStream @@ -170,6 +173,14 @@ class DemoFragment : BaseServiceDependentMainMenuFragment(), DemoAdapter.OnDemoI getString(R.string.dev_kit_sensor_917_desc) ) ) + + add( + WiFiThroughput( + R.drawable.redesign_ic_demo_wifi_throughput, + getString(R.string.wifi_title_Throughput), + getString(R.string.wifi_main_menu_description_wifi_throughput) + ) + ) } } @@ -238,6 +249,7 @@ class DemoFragment : BaseServiceDependentMainMenuFragment(), DemoAdapter.OnDemoI } } + override fun onDemoItemClicked(demoItem: DemoMenuItem) { /* if (demoItem.connectType == BluetoothService.GattConnectType.DEV_KIT_SENSOR) { if (isNetworkAvailable(context)) { @@ -279,6 +291,26 @@ class DemoFragment : BaseServiceDependentMainMenuFragment(), DemoAdapter.OnDemoI Toast.LENGTH_SHORT ).show() } + } else if(demoItem.connectType == BluetoothService.GattConnectType.WIFI_THROUGHPUT_TEST){ + if (isNetworkAvailable(context)) { + /*GlobalScope.launch(Dispatchers.Main) { + val result = withContext(Dispatchers.IO) { + ThroughputUtils.sendEvent() + } + }*/ + requireContext().startActivity( + Intent( + requireContext(), + WifiThroughputActivity::class.java + ) + ) + }else { + Toast.makeText( + requireContext(), + getString(R.string.turn_on_wifi), + Toast.LENGTH_SHORT + ).show() + } } else { println("BLE_PROV demoItem:${demoItem.connectType}") selectDeviceDialog = SelectDeviceDialog.newDialog(demoItem.connectType) diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/TestFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/TestFragment.kt index 22f97ed8..c1ebfc33 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/TestFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/TestFragment.kt @@ -11,6 +11,7 @@ import com.siliconlabs.bledemo.features.iop_test.dialogs.AboutIopDialog import com.siliconlabs.bledemo.home_screen.base.BaseServiceDependentMainMenuFragment import com.siliconlabs.bledemo.home_screen.base.BluetoothDependent import com.siliconlabs.bledemo.home_screen.base.LocationDependent +import com.siliconlabs.bledemo.home_screen.base.NotificationDependent class TestFragment : BaseServiceDependentMainMenuFragment(), DialogInterface.OnDismissListener { @@ -107,6 +108,8 @@ class TestFragment : BaseServiceDependentMainMenuFragment(), DialogInterface.OnD } } + + override fun onDismiss(dialogInterface: DialogInterface) { selectDeviceDialog = null } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/menu_items/ConnectedLighting.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/menu_items/ConnectedLighting.kt index ddb86b52..18e74cbb 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/menu_items/ConnectedLighting.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/menu_items/ConnectedLighting.kt @@ -2,7 +2,7 @@ package com.siliconlabs.bledemo.home_screen.menu_items import com.siliconlabs.bledemo.bluetooth.services.BluetoothService -class ConnectedLighting(imageResId: Int, title: String, description: String) : DemoMenuItem(imageResId, title, description) { - +class ConnectedLighting(imageResId: Int, title: String, description: String) : + DemoMenuItem(imageResId, title, description) { override val connectType = BluetoothService.GattConnectType.LIGHT } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/menu_items/WiFiThroughput.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/menu_items/WiFiThroughput.kt new file mode 100644 index 00000000..0a05b6c4 --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/menu_items/WiFiThroughput.kt @@ -0,0 +1,8 @@ +package com.siliconlabs.bledemo.home_screen.menu_items + +import com.siliconlabs.bledemo.bluetooth.services.BluetoothService + +//Sachith +class WiFiThroughput(imageResId: Int, title: String, description: String) : DemoMenuItem(imageResId, title, description) { + override val connectType = BluetoothService.GattConnectType.WIFI_THROUGHPUT_TEST +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/viewmodels/MainActivityViewModel.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/viewmodels/MainActivityViewModel.kt index 4bafe31f..a1720336 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/viewmodels/MainActivityViewModel.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/viewmodels/MainActivityViewModel.kt @@ -1,5 +1,6 @@ package com.siliconlabs.bledemo.home_screen.viewmodels +import androidx.core.content.ContextCompat import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -18,6 +19,12 @@ class MainActivityViewModel : ViewModel() { private val _isSetupFinished: MutableLiveData = MutableLiveData() val isSetupFinished: LiveData = _isSetupFinished + private val _isNotificationOn: MutableLiveData = MutableLiveData() + val isNotificationOn: LiveData = _isNotificationOn + + private val _isNotificationPermissionGranted = MutableLiveData() + val isNotificationPermissionGranted: LiveData get() = _isNotificationPermissionGranted + fun setIsBluetoothOn(isBluetoothOn: Boolean) { _isBluetoothOn.postValue(isBluetoothOn) } @@ -38,8 +45,19 @@ class MainActivityViewModel : ViewModel() { _isSetupFinished.postValue(isSetupFinished) } + fun setIsNotificationOn(isNotificationOn: Boolean) { + _isNotificationOn.postValue(isNotificationOn) + } + + fun setIsNotificationPermissionGranted(isGranted: Boolean){ + _isNotificationPermissionGranted.postValue(isGranted) + } + + fun getIsBluetoothOn(): Boolean = _isBluetoothOn.value ?: false fun getIsLocationPermissionGranted(): Boolean = _isLocationPermissionGranted.value ?: false fun getAreBluetoothPermissionsGranted(): Boolean = _areBluetoothPermissionsGranted.value ?: false fun getIsSetupFinished(): Boolean = _isSetupFinished.value ?: false + + } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/viewmodels/ScanFragmentViewModel.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/viewmodels/ScanFragmentViewModel.kt index bf436c9c..3ad82fe8 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/viewmodels/ScanFragmentViewModel.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/viewmodels/ScanFragmentViewModel.kt @@ -221,7 +221,10 @@ class ScanFragmentViewModel(private val context: Context) : ScannerViewModel() { fun toggleViewExpansion(position: Int) { _filteredDevices.value = _filteredDevices.value?.apply { - set(position, this[position].copy(isBluetoothInfoExpanded = !this[position].isBluetoothInfoExpanded)) + println("SICONNECT --this$this") + println("SICONNECT --isBluetoothInfoExpanded${this[position].isBluetoothInfoExpanded}") + set(position, this[position] + .copy(isBluetoothInfoExpanded = !this[position].isBluetoothInfoExpanded)) } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/views/NoServiceWarningBar.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/views/NoServiceWarningBar.kt index eef05ebc..613c1af3 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/views/NoServiceWarningBar.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/views/NoServiceWarningBar.kt @@ -7,10 +7,12 @@ import android.provider.Settings import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout +import androidx.core.content.ContextCompat.startActivity import androidx.fragment.app.FragmentManager import com.siliconlabs.bledemo.databinding.NoServiceWarningBarBinding import com.siliconlabs.bledemo.home_screen.dialogs.WarningBarInfoDialog + abstract class NoServiceWarningBar(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { protected val _binding = NoServiceWarningBarBinding.inflate(LayoutInflater.from(context), this, true) @@ -31,6 +33,13 @@ abstract class NoServiceWarningBar(context: Context, attrs: AttributeSet?) : Lin .also { context.startActivity(it) } } + protected fun showAppNotificationSettingsScreen() { + val intent = Intent() + intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS) + intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName()) + context.startActivity(intent) + } + protected fun showInfoDialog(type: WarningBarInfoDialog.Type) { fragmentManager?.let { WarningBarInfoDialog(type).show(it, "warning_bar_info_dialog") diff --git a/mobile/src/main/jniLibs/arm64-v8a/libCHIPController.so b/mobile/src/main/jniLibs/arm64-v8a/libCHIPController.so index c3d04809..01c51ddd 100644 --- a/mobile/src/main/jniLibs/arm64-v8a/libCHIPController.so +++ b/mobile/src/main/jniLibs/arm64-v8a/libCHIPController.so @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd0803645d5ce23a8e310b8961270a2d78566d899945607ad450e8eec8a0d3cf -size 366663064 +oid sha256:63abfa9662fbfa253121b5c20891d144ccc6b95702e738c187509d2e0bedc92e +size 90085952 diff --git a/mobile/src/main/jniLibs/arm64-v8a/libc++_shared.so b/mobile/src/main/jniLibs/arm64-v8a/libc++_shared.so index c7eca00e..e08eb3cd 100644 --- a/mobile/src/main/jniLibs/arm64-v8a/libc++_shared.so +++ b/mobile/src/main/jniLibs/arm64-v8a/libc++_shared.so @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f8ccc2038a0774cde288d8e58390236ad2605aad6515dfa0d146647cf40ddbb0 -size 6933952 +oid sha256:9b53fecf513fa050dc28457c7a61d7c31d84f7f72059a2c95053b59eae4d98a8 +size 6937312 diff --git a/mobile/src/main/jniLibs/armeabi-v7a/libCHIPController.so b/mobile/src/main/jniLibs/armeabi-v7a/libCHIPController.so index 46c2e9dd..77c461c6 100644 --- a/mobile/src/main/jniLibs/armeabi-v7a/libCHIPController.so +++ b/mobile/src/main/jniLibs/armeabi-v7a/libCHIPController.so @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd1667fbd40f7bffa27eb9f181585abd3d5b5bbfa91973bd1638afede9517e4e -size 352035880 +oid sha256:91221a4eb1b66249182c4b0b6597fabd70bc0e0440f0c7e78c85d2526f97090f +size 84263980 diff --git a/mobile/src/main/jniLibs/armeabi-v7a/libc++_shared.so b/mobile/src/main/jniLibs/armeabi-v7a/libc++_shared.so index 3200ef2e..54bdf1a9 100644 --- a/mobile/src/main/jniLibs/armeabi-v7a/libc++_shared.so +++ b/mobile/src/main/jniLibs/armeabi-v7a/libc++_shared.so @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52df819dc40888fb2e736f4cb1e51329afcd9aec6dbcab4fa6a1885a3b84e844 -size 4359916 +oid sha256:22b3ed78887dca77ab2bb39c6633096d5605ab697d1ecd963b3fd628a1c21ade +size 4364084 diff --git a/mobile/src/main/jniLibs/x86/libCHIPController.so b/mobile/src/main/jniLibs/x86/libCHIPController.so index 6e8ca513..062bf7d5 100644 --- a/mobile/src/main/jniLibs/x86/libCHIPController.so +++ b/mobile/src/main/jniLibs/x86/libCHIPController.so @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:640e3d5b4f1d3667a397b60e910cc368790b3d44356c5267ae660e9a3ecf3efb -size 354130728 +oid sha256:830563a2f221f135dab22da208e56eed30089de1880c07944bbaff4d0b2ce593 +size 87241828 diff --git a/mobile/src/main/jniLibs/x86/libc++_shared.so b/mobile/src/main/jniLibs/x86/libc++_shared.so index 8efb21f2..b7e0c8d7 100644 --- a/mobile/src/main/jniLibs/x86/libc++_shared.so +++ b/mobile/src/main/jniLibs/x86/libc++_shared.so @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b57f851b2412a16b88f2ceae8d2ce52c5dc0ea98819b58edf63e1f8abb8c1d0 -size 5645372 +oid sha256:2abfeb5a847ec34d0e9d8c696a8e83072fe3a81e402f4dd3b7a01b374edf59e6 +size 5649592 diff --git a/mobile/src/main/jniLibs/x86_64/libCHIPController.so b/mobile/src/main/jniLibs/x86_64/libCHIPController.so index c2033628..cea01ecf 100644 --- a/mobile/src/main/jniLibs/x86_64/libCHIPController.so +++ b/mobile/src/main/jniLibs/x86_64/libCHIPController.so @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a8075d839d14ff925639b97ec6e9f64ef6713517b2fb1393a7d99e4636cba9f -size 360863976 +oid sha256:63f940936f5859ff7204c5ac403b066f091ddfd5a11e5afd3502fe26b3720f6d +size 87232328 diff --git a/mobile/src/main/jniLibs/x86_64/libc++_shared.so b/mobile/src/main/jniLibs/x86_64/libc++_shared.so index 0121bab1..39c4ec2c 100644 --- a/mobile/src/main/jniLibs/x86_64/libc++_shared.so +++ b/mobile/src/main/jniLibs/x86_64/libc++_shared.so @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22b0fa946465327af7ad84a32787595d03d2667b53f2304ec72f0a80b34fc393 -size 6716064 +oid sha256:f7dc30aa161b6eb889146ee518ee8a0d1da2d0cc965cd31d31cd39aae11e349a +size 6719376 diff --git a/mobile/src/main/res/drawable/background_blue_box.xml b/mobile/src/main/res/drawable/background_blue_box.xml new file mode 100644 index 00000000..580a11d6 --- /dev/null +++ b/mobile/src/main/res/drawable/background_blue_box.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/mobile/src/main/res/drawable/btn_rounded_blue.xml b/mobile/src/main/res/drawable/btn_rounded_blue.xml index 56cd2399..b4dbeb5c 100644 --- a/mobile/src/main/res/drawable/btn_rounded_blue.xml +++ b/mobile/src/main/res/drawable/btn_rounded_blue.xml @@ -3,7 +3,7 @@ - + @@ -11,7 +11,7 @@ - + diff --git a/mobile/src/main/res/drawable/dishwasher_state_on.xml b/mobile/src/main/res/drawable/dishwasher_state_on.xml new file mode 100644 index 00000000..13f96678 --- /dev/null +++ b/mobile/src/main/res/drawable/dishwasher_state_on.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + diff --git a/mobile/src/main/res/drawable/ic_dishwasher_icon_stateoff.xml b/mobile/src/main/res/drawable/ic_dishwasher_icon_stateoff.xml new file mode 100644 index 00000000..3e7df041 --- /dev/null +++ b/mobile/src/main/res/drawable/ic_dishwasher_icon_stateoff.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + diff --git a/mobile/src/main/res/drawable/ic_dishwasher_icon_stateon.xml b/mobile/src/main/res/drawable/ic_dishwasher_icon_stateon.xml new file mode 100644 index 00000000..4263d280 --- /dev/null +++ b/mobile/src/main/res/drawable/ic_dishwasher_icon_stateon.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + diff --git a/mobile/src/main/res/drawable/ic_electric_dish_washer.xml b/mobile/src/main/res/drawable/ic_electric_dish_washer.xml new file mode 100644 index 00000000..e21c2286 --- /dev/null +++ b/mobile/src/main/res/drawable/ic_electric_dish_washer.xml @@ -0,0 +1,9 @@ + + + diff --git a/mobile/src/main/res/drawable/matter_dishwasher_list.xml b/mobile/src/main/res/drawable/matter_dishwasher_list.xml new file mode 100644 index 00000000..41bdb034 --- /dev/null +++ b/mobile/src/main/res/drawable/matter_dishwasher_list.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + diff --git a/mobile/src/main/res/drawable/matter_dishwasher_off.xml b/mobile/src/main/res/drawable/matter_dishwasher_off.xml new file mode 100644 index 00000000..69e651c0 --- /dev/null +++ b/mobile/src/main/res/drawable/matter_dishwasher_off.xml @@ -0,0 +1,9 @@ + + + diff --git a/mobile/src/main/res/drawable/matter_dishwasher_on.xml b/mobile/src/main/res/drawable/matter_dishwasher_on.xml new file mode 100644 index 00000000..d8da7f39 --- /dev/null +++ b/mobile/src/main/res/drawable/matter_dishwasher_on.xml @@ -0,0 +1,9 @@ + + + diff --git a/mobile/src/main/res/drawable/progress_bar_right_to_left.xml b/mobile/src/main/res/drawable/progress_bar_right_to_left.xml new file mode 100644 index 00000000..c8b1bfce --- /dev/null +++ b/mobile/src/main/res/drawable/progress_bar_right_to_left.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/mobile/src/main/res/drawable/redesign_ic_demo_wifi_throughput.xml b/mobile/src/main/res/drawable/redesign_ic_demo_wifi_throughput.xml new file mode 100644 index 00000000..a165994c --- /dev/null +++ b/mobile/src/main/res/drawable/redesign_ic_demo_wifi_throughput.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/mobile/src/main/res/drawable/w0.png b/mobile/src/main/res/drawable/w0.png new file mode 100644 index 00000000..af7aea58 Binary files /dev/null and b/mobile/src/main/res/drawable/w0.png differ diff --git a/mobile/src/main/res/drawable/w1.png b/mobile/src/main/res/drawable/w1.png new file mode 100644 index 00000000..2b0760a6 Binary files /dev/null and b/mobile/src/main/res/drawable/w1.png differ diff --git a/mobile/src/main/res/drawable/w10.png b/mobile/src/main/res/drawable/w10.png new file mode 100644 index 00000000..ff9d8f6b Binary files /dev/null and b/mobile/src/main/res/drawable/w10.png differ diff --git a/mobile/src/main/res/drawable/w2.png b/mobile/src/main/res/drawable/w2.png new file mode 100644 index 00000000..61a81a86 Binary files /dev/null and b/mobile/src/main/res/drawable/w2.png differ diff --git a/mobile/src/main/res/drawable/w3.png b/mobile/src/main/res/drawable/w3.png new file mode 100644 index 00000000..cd4a7082 Binary files /dev/null and b/mobile/src/main/res/drawable/w3.png differ diff --git a/mobile/src/main/res/drawable/w4.png b/mobile/src/main/res/drawable/w4.png new file mode 100644 index 00000000..a7ea9c9b Binary files /dev/null and b/mobile/src/main/res/drawable/w4.png differ diff --git a/mobile/src/main/res/drawable/w5.png b/mobile/src/main/res/drawable/w5.png new file mode 100644 index 00000000..6069171b Binary files /dev/null and b/mobile/src/main/res/drawable/w5.png differ diff --git a/mobile/src/main/res/drawable/w6.png b/mobile/src/main/res/drawable/w6.png new file mode 100644 index 00000000..6bcb94c1 Binary files /dev/null and b/mobile/src/main/res/drawable/w6.png differ diff --git a/mobile/src/main/res/drawable/w7.png b/mobile/src/main/res/drawable/w7.png new file mode 100644 index 00000000..1d640a8b Binary files /dev/null and b/mobile/src/main/res/drawable/w7.png differ diff --git a/mobile/src/main/res/drawable/w8.png b/mobile/src/main/res/drawable/w8.png new file mode 100644 index 00000000..798a763f Binary files /dev/null and b/mobile/src/main/res/drawable/w8.png differ diff --git a/mobile/src/main/res/drawable/w9.png b/mobile/src/main/res/drawable/w9.png new file mode 100644 index 00000000..75df6b18 Binary files /dev/null and b/mobile/src/main/res/drawable/w9.png differ diff --git a/mobile/src/main/res/layout/activity_917_dev_kit_sensor_layout.xml b/mobile/src/main/res/layout/activity_917_dev_kit_sensor_layout.xml index 38e22c19..4543db08 100644 --- a/mobile/src/main/res/layout/activity_917_dev_kit_sensor_layout.xml +++ b/mobile/src/main/res/layout/activity_917_dev_kit_sensor_layout.xml @@ -1,8 +1,8 @@ @@ -32,27 +32,64 @@ - + app:layout_constraintTop_toTopOf="@+id/env_grid_place"> - + android:gravity="center" + android:padding="@dimen/adapter_item_padding" + android:text="@string/matter_scanner_adapter_empty_list_title" + android:textColor="@color/adapter_item_title_color" + android:textSize="@dimen/text_size_L" /> + + + +
+ + + + - + - + \ No newline at end of file diff --git a/mobile/src/main/res/layout/activity_advertiser_config.xml b/mobile/src/main/res/layout/activity_advertiser_config.xml index 842467ef..6df2e34c 100644 --- a/mobile/src/main/res/layout/activity_advertiser_config.xml +++ b/mobile/src/main/res/layout/activity_advertiser_config.xml @@ -29,13 +29,21 @@ android:orientation="vertical" android:padding="16dp"> - + - + - + - +
diff --git a/mobile/src/main/res/layout/activity_environment.xml b/mobile/src/main/res/layout/activity_environment.xml index 9077d44a..53a392ce 100644 --- a/mobile/src/main/res/layout/activity_environment.xml +++ b/mobile/src/main/res/layout/activity_environment.xml @@ -5,9 +5,8 @@ + app:titleTextColor="#ffff" /> diff --git a/mobile/src/main/res/layout/activity_motion.xml b/mobile/src/main/res/layout/activity_motion.xml index 03f93cd9..ca94ff90 100644 --- a/mobile/src/main/res/layout/activity_motion.xml +++ b/mobile/src/main/res/layout/activity_motion.xml @@ -16,6 +16,7 @@ android:paddingRight="@dimen/iodemo_margin" /> + + + + \ No newline at end of file diff --git a/mobile/src/main/res/layout/adapter_advertiser.xml b/mobile/src/main/res/layout/adapter_advertiser.xml index 371b6f68..3767687b 100644 --- a/mobile/src/main/res/layout/adapter_advertiser.xml +++ b/mobile/src/main/res/layout/adapter_advertiser.xml @@ -260,8 +260,8 @@ diff --git a/mobile/src/main/res/layout/adapter_gatt_server.xml b/mobile/src/main/res/layout/adapter_gatt_server.xml index d59d3205..e2d183a1 100644 --- a/mobile/src/main/res/layout/adapter_gatt_server.xml +++ b/mobile/src/main/res/layout/adapter_gatt_server.xml @@ -138,8 +138,8 @@ diff --git a/mobile/src/main/res/layout/advertiser_config_name.xml b/mobile/src/main/res/layout/advertiser_config_name.xml index bceac55c..fa5b7ade 100644 --- a/mobile/src/main/res/layout/advertiser_config_name.xml +++ b/mobile/src/main/res/layout/advertiser_config_name.xml @@ -10,6 +10,7 @@ android:paddingTop="16dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + android:textSize="@dimen/text_size_SM" /> + + + + + + + + + + + + + + + + + diff --git a/mobile/src/main/res/layout/dialog_network_selection_matter.xml b/mobile/src/main/res/layout/dialog_network_selection_matter.xml index f71e543a..001c954f 100644 --- a/mobile/src/main/res/layout/dialog_network_selection_matter.xml +++ b/mobile/src/main/res/layout/dialog_network_selection_matter.xml @@ -15,7 +15,9 @@ android:layout_gravity="center" android:gravity="center" android:layout_marginTop="7dp" + android:textColor="@color/silabs_primary_text" android:text="@string/matter_network_selection_alert_title" + android:fontFamily="sans-serif-black" android:textSize="@dimen/text_size_ML" android:textStyle="bold" /> @@ -26,8 +28,10 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" + android:textStyle="bold" + android:fontFamily="sans-serif-black" android:text="@string/matter_select_network_mode_alert_message" - android:textSize="@dimen/text_size_M" /> + android:textSize="@dimen/text_size_SM" /> + android:background="@color/silabs_divider" /> @@ -66,7 +71,7 @@ + android:background="@color/silabs_divider" /> + android:background="@color/silabs_divider" /> diff --git a/mobile/src/main/res/layout/fragment_browser.xml b/mobile/src/main/res/layout/fragment_browser.xml index e8f18654..a78be913 100644 --- a/mobile/src/main/res/layout/fragment_browser.xml +++ b/mobile/src/main/res/layout/fragment_browser.xml @@ -41,7 +41,6 @@ android:visibility="gone" tools:visibility="visible" /> - + + + + + + + + + + + + + + + + +