Skip to content

Commit

Permalink
feat(android): move bluetooth connection to service
Browse files Browse the repository at this point in the history
This involved major refactoring. Main improvements are:
- Single activity
- Device disconnect handling
- Lower level composables are now tested rather than the root composable
  • Loading branch information
Oppzippy committed Jun 30, 2023
1 parent 4522667 commit 1284cd8
Show file tree
Hide file tree
Showing 65 changed files with 1,353 additions and 845 deletions.
8 changes: 5 additions & 3 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ android {

defaultConfig {
applicationId = "com.oppzippy.openscq30"
minSdk = 24
minSdk = 26
targetSdk = 33
versionCode = 6
versionName = "1.3.1"
Expand Down Expand Up @@ -105,7 +105,9 @@ android {
}

dependencies {
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
val lifecycleVersion = "2.6.1"
implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion")
implementation("androidx.lifecycle:lifecycle-service:$lifecycleVersion")

// Compose
val composeBomVersion = "2023.06.00"
Expand All @@ -131,7 +133,7 @@ dependencies {
// Optional - Integration with activities
implementation("androidx.activity:activity-compose:1.7.2")
// Optional - Integration with ViewModels
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycleVersion")
// Compose navigation
implementation("androidx.navigation:navigation-compose:2.5.3")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package com.oppzippy.openscq30

import androidx.compose.ui.test.*
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assertHasClickAction
import androidx.compose.ui.test.hasContentDescriptionExactly
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import com.oppzippy.openscq30.features.ui.deviceselection.composables.DeviceSelectionPermissionCheck
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import com.oppzippy.openscq30.features.bluetoothdeviceprovider.BluetoothDevice
import com.oppzippy.openscq30.features.bluetoothdeviceprovider.BluetoothDeviceProvider
import com.oppzippy.openscq30.ui.deviceselection.DeviceSelectionRoot
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import io.mockk.every
import io.mockk.junit4.MockKRule
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import javax.inject.Inject

@HiltAndroidTest
class DeviceSelectionActivityViewTest {
Expand All @@ -23,10 +27,7 @@ class DeviceSelectionActivityViewTest {
val hiltRule = HiltAndroidRule(this)

@get:Rule(order = 2)
val composeRule = createAndroidComposeRule<MainActivity>()

@Inject
lateinit var deviceProviderMock: BluetoothDeviceProvider
val composeRule = createAndroidComposeRule<TestActivity>()

private lateinit var noDevicesFound: SemanticsMatcher
private lateinit var refreshButton: SemanticsMatcher
Expand All @@ -43,12 +44,9 @@ class DeviceSelectionActivityViewTest {

@Test
fun testWithNoDevices() {
every { deviceProviderMock.getDevices() } returns listOf()

composeRule.setContent {
DeviceSelectionPermissionCheck(
bluetoothDeviceProvider = deviceProviderMock,
onInfoClick = {},
DeviceSelectionRoot(
devices = emptyList(),
)
}

Expand All @@ -61,12 +59,10 @@ class DeviceSelectionActivityViewTest {
BluetoothDevice("test1", "00:00:00:00:00:00"),
BluetoothDevice("test2", "00:00:00:00:00:01"),
)
every { deviceProviderMock.getDevices() } returns deviceModels

composeRule.setContent {
DeviceSelectionPermissionCheck(
bluetoothDeviceProvider = deviceProviderMock,
onInfoClick = {},
DeviceSelectionRoot(
devices = deviceModels,
)
}

Expand All @@ -80,30 +76,33 @@ class DeviceSelectionActivityViewTest {

@Test
fun testWithNoDevicesAndThenRefreshWithDevices() {
every { deviceProviderMock.getDevices() } returns listOf()
var devices: List<BluetoothDevice> = emptyList()
val devicesFlow = MutableStateFlow(devices)

composeRule.setContent {
DeviceSelectionPermissionCheck(
bluetoothDeviceProvider = deviceProviderMock,
onInfoClick = {},
DeviceSelectionRoot(
devices = devicesFlow.collectAsState().value,
onRefreshDevices = {
devicesFlow.value = devices
}
)
}

composeRule.onNode(noDevicesFound).assertExists()

val deviceModels = listOf(
devices = listOf(
BluetoothDevice("test", "00:00:00:00:00:00"),
)
every { deviceProviderMock.getDevices() } returns deviceModels
composeRule.onNode(noDevicesFound).assertExists()

composeRule.onNode(refreshButton).performClick()

deviceModels.forEach {
devices.forEach {
composeRule.onNodeWithText(it.name).assertExists().assertHasClickAction()
composeRule.onNodeWithText(it.address).assertExists().assertHasClickAction()
}

composeRule.onNode(noDevicesFound).assertDoesNotExist()
}

}
}
Loading

0 comments on commit 1284cd8

Please sign in to comment.