diff options
| author | Albin <albin@mullvad.net> | 2021-10-13 18:14:45 +0200 |
|---|---|---|
| committer | Albin <albin@mullvad.net> | 2021-12-16 11:38:25 +0100 |
| commit | 6c4d31dfc59772e9a80d0260fee34cf03d5c05f5 (patch) | |
| tree | 8418020b852073a6dfb0c4c44fabdb1586404d11 /android/app/src/test | |
| parent | 4dcdb1306d23a8e831389130fdef452cae4dc809 (diff) | |
| download | mullvadvpn-6c4d31dfc59772e9a80d0260fee34cf03d5c05f5.tar.xz mullvadvpn-6c4d31dfc59772e9a80d0260fee34cf03d5c05f5.zip | |
Split Android project and app module
The purpose of this is to:
* Comply better with the default Android project structure
(see https://developer.android.com/studio/build).
* Avoid conflicts between project and app dependencies and plugins.
Diffstat (limited to 'android/app/src/test')
7 files changed, 646 insertions, 0 deletions
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestCoroutineRule.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestCoroutineRule.kt new file mode 100644 index 0000000000..1acdf9e577 --- /dev/null +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestCoroutineRule.kt @@ -0,0 +1,24 @@ +package net.mullvad.mullvadvpn + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.rules.TestWatcher +import org.junit.runner.Description + +class TestCoroutineRule( + val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher() +) : TestWatcher() { + + override fun starting(description: Description?) { + super.starting(description) + Dispatchers.setMain(testDispatcher) + } + + override fun finished(description: Description?) { + super.finished(description) + Dispatchers.resetMain() + testDispatcher.cleanupTestCoroutines() + } +} diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestUtils.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestUtils.kt new file mode 100644 index 0000000000..4c4f043c06 --- /dev/null +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestUtils.kt @@ -0,0 +1,11 @@ +package net.mullvad.mullvadvpn + +import kotlin.test.assertTrue + +fun <T> assertLists(expected: List<T>, actual: List<T>, message: String? = null) = assertTrue( + expected.size == actual.size && expected.containsAll(actual) && actual.containsAll(expected), + message ?: """Expected list should have same size and contains same items. + | Expected(${expected.size}): $expected + | Actual(${actual.size}) : $actual + """.trimMargin() +) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt new file mode 100644 index 0000000000..e6d43621a1 --- /dev/null +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt @@ -0,0 +1,98 @@ +package net.mullvad.mullvadvpn.applist + +import android.content.pm.PackageManager +import android.graphics.drawable.Drawable +import android.os.Looper +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import io.mockk.verify +import kotlin.test.assertEquals +import kotlin.test.assertFails +import org.junit.After +import org.junit.Before +import org.junit.Test + +class ApplicationsIconManagerTest { + private val mockedPackageManager = mockk<PackageManager>() + private val mockedMainLooper = mockk<Looper>() + private val testSubject = ApplicationsIconManager(mockedPackageManager) + + @Before + fun setUp() { + mockkStatic(Looper::class) + every { Looper.getMainLooper() } returns mockedMainLooper + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun test_first_time_load_icon_from_PM() { + val testPackageName = "test" + val mockedDrawable = mockk<Drawable>() + every { mockedPackageManager.getApplicationIcon(testPackageName) } returns mockedDrawable + every { mockedMainLooper.isCurrentThread } returns false + + val result = testSubject.getAppIcon(testPackageName) + + assertEquals(mockedDrawable, result) + verify { + mockedMainLooper.isCurrentThread + mockedPackageManager.getApplicationIcon(testPackageName) + } + } + + @Test + fun test_second_time_load_icon_from_cache() { + val testPackageName = "test" + val mockedDrawable = mockk<Drawable>() + every { mockedPackageManager.getApplicationIcon(testPackageName) } returns mockedDrawable + every { mockedMainLooper.isCurrentThread } returns false + + val result = testSubject.getAppIcon(testPackageName) + val result2 = testSubject.getAppIcon(testPackageName) + + assertEquals(mockedDrawable, result) + assertEquals(mockedDrawable, result2) + verify(exactly = 2) { + mockedMainLooper.isCurrentThread + } + verify(exactly = 1) { + mockedPackageManager.getApplicationIcon(testPackageName) + } + } + + @Test + fun test_second_time_load_icon_from_PM_after_clear() { + val testPackageName = "test" + val mockedDrawable = mockk<Drawable>() + every { mockedPackageManager.getApplicationIcon(testPackageName) } returns mockedDrawable + every { mockedMainLooper.isCurrentThread } returns false + + val result = testSubject.getAppIcon(testPackageName) + testSubject.dispose() + val result2 = testSubject.getAppIcon(testPackageName) + + assertEquals(mockedDrawable, result) + assertEquals(mockedDrawable, result2) + verify(exactly = 2) { + mockedMainLooper.isCurrentThread + mockedPackageManager.getApplicationIcon(testPackageName) + } + } + + @Test + fun test_throw_exception_when_invoke_from_MainThread() { + val testPackageName = "test" + every { mockedMainLooper.isCurrentThread } returns true + + assertFails("Should not be called from MainThread") { + testSubject.getAppIcon(testPackageName) + } + verify { mockedMainLooper.isCurrentThread } + } +} diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt new file mode 100644 index 0000000000..e1a9e37ac4 --- /dev/null +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt @@ -0,0 +1,129 @@ +package net.mullvad.mullvadvpn.applist + +import android.Manifest +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import io.mockk.every +import io.mockk.mockk +import io.mockk.unmockkAll +import io.mockk.verifyAll +import net.mullvad.mullvadvpn.assertLists +import org.junit.After +import org.junit.Test + +class ApplicationsProviderTest { + private val mockedPackageManager = mockk<PackageManager>() + private val selfPackageName = "self_package_name" + private val testSubject = ApplicationsProvider(mockedPackageManager, selfPackageName) + private val internet = Manifest.permission.INTERNET + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun test_get_apps() { + val launchWithInternetPackageName = "launch_with_internet_package_name" + val launchWithoutInternetPackageName = "launch_without_internet_package_name" + val nonLaunchWithInternetPackageName = "non_launch_with_internet_package_name" + val nonLaunchWithoutInternetPackageName = "non_launch_without_internet_package_name" + val leanbackLaunchWithInternetPackageName = "leanback_launch_with_internet_package_name" + val leanbackLaunchWithoutInternetPackageName = + "leanback_launch_without_internet_package_name" + + every { + mockedPackageManager.getInstalledApplications(PackageManager.GET_META_DATA) + } returns listOf( + createApplicationInfo(launchWithInternetPackageName, launch = true, internet = true), + createApplicationInfo(launchWithoutInternetPackageName, launch = true), + createApplicationInfo(nonLaunchWithInternetPackageName, internet = true), + createApplicationInfo(nonLaunchWithoutInternetPackageName), + createApplicationInfo( + leanbackLaunchWithInternetPackageName, + leanback = true, + internet = true + ), + createApplicationInfo(leanbackLaunchWithoutInternetPackageName, leanback = true), + createApplicationInfo(selfPackageName, internet = true, launch = true) + ) + + val result = testSubject.getAppsList() + val expected = listOf( + AppData(launchWithInternetPackageName, 0, launchWithInternetPackageName), + AppData(nonLaunchWithInternetPackageName, 0, nonLaunchWithInternetPackageName, true), + AppData(leanbackLaunchWithInternetPackageName, 0, leanbackLaunchWithInternetPackageName) + ) + + assertLists(expected, result) + + verifyAll { + mockedPackageManager.getInstalledApplications(PackageManager.GET_META_DATA) + + listOf( + launchWithInternetPackageName, + launchWithoutInternetPackageName, + nonLaunchWithInternetPackageName, + nonLaunchWithoutInternetPackageName, + leanbackLaunchWithInternetPackageName, + leanbackLaunchWithoutInternetPackageName, + selfPackageName + ).forEach { packageName -> + mockedPackageManager.checkPermission(internet, packageName) + } + + listOf( + launchWithInternetPackageName, + nonLaunchWithInternetPackageName, + leanbackLaunchWithInternetPackageName + ).forEach { packageName -> + mockedPackageManager.getLaunchIntentForPackage(packageName) + } + + listOf( + nonLaunchWithInternetPackageName, + leanbackLaunchWithInternetPackageName, + ).forEach { packageName -> + mockedPackageManager.getLeanbackLaunchIntentForPackage(packageName) + } + } + } + + private fun createApplicationInfo( + packageName: String, + launch: Boolean = false, + leanback: Boolean = false, + internet: Boolean = false, + systemApp: Boolean = false + ): ApplicationInfo { + val mockApplicationInfo = mockk<ApplicationInfo>() + + mockApplicationInfo.packageName = packageName + mockApplicationInfo.icon = 0 + + every { mockApplicationInfo.loadLabel(mockedPackageManager) } returns packageName + + every { + mockedPackageManager.getLaunchIntentForPackage(packageName) + } returns if (launch || systemApp) + mockk() + else + null + + every { + mockedPackageManager.getLeanbackLaunchIntentForPackage(packageName) + } returns if (leanback || systemApp) + mockk() + else + null + + every { + mockedPackageManager.checkPermission(Manifest.permission.INTERNET, packageName) + } returns if (internet) + PackageManager.PERMISSION_GRANTED + else + PackageManager.PERMISSION_DENIED + + return mockApplicationInfo + } +} diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/di/AppModuleTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/di/AppModuleTest.kt new file mode 100644 index 0000000000..c30a63fedf --- /dev/null +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/di/AppModuleTest.kt @@ -0,0 +1,49 @@ +package net.mullvad.mullvadvpn.di + +import android.os.Messenger +import io.mockk.mockk +import io.mockk.unmockkAll +import kotlin.test.assertEquals +import net.mullvad.mullvadvpn.ipc.Event +import net.mullvad.mullvadvpn.ipc.MessageDispatcher +import net.mullvad.mullvadvpn.ui.serviceconnection.SplitTunneling +import org.junit.After +import org.junit.Rule +import org.junit.Test +import org.koin.core.parameter.parametersOf +import org.koin.core.qualifier.named +import org.koin.core.scope.Scope +import org.koin.test.KoinTest +import org.koin.test.KoinTestRule + +class AppModuleTest : KoinTest { + + @get:Rule + val koinTestRule = KoinTestRule.create { + modules(appModule) + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun test_scope_linking() { + val appsScope: Scope = getKoin().createScope(APPS_SCOPE, named(APPS_SCOPE)) + val serviceConnectionScope = getKoin().createScope( + SERVICE_CONNECTION_SCOPE, + named(SERVICE_CONNECTION_SCOPE) + ) + + appsScope.linkTo(serviceConnectionScope) + + val mockedMessenger = mockk<Messenger>() + val mockedEventMessageHandler = mockk<MessageDispatcher<Event>>(relaxed = true) + val serviceConnectionSplitTunneling = serviceConnectionScope.get<SplitTunneling>( + parameters = { parametersOf(mockedMessenger, mockedEventMessageHandler) } + ) + + assertEquals(appsScope.get<SplitTunneling>(), serviceConnectionSplitTunneling) + } +} diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparatorTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparatorTest.kt new file mode 100644 index 0000000000..a3c96349d9 --- /dev/null +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparatorTest.kt @@ -0,0 +1,97 @@ +package net.mullvad.mullvadvpn.relaylist + +import io.mockk.mockk +import io.mockk.unmockkAll +import org.junit.After +import org.junit.Assert.assertTrue +import org.junit.Test + +class RelayNameComparatorTest { + + private val mockedCity = mockk<RelayCity>(relaxed = true) + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun test_compare_respect_numbers_in_name() { + val relay9 = Relay(mockedCity, "se9-wireguard", false) + val relay10 = Relay(mockedCity, "se10-wireguard", false) + + relay9 assertOrderBothDirection relay10 + } + + @Test + fun test_compare_same_name() { + val relay9a = Relay(mockedCity, "se9-wireguard", false) + val relay9b = Relay(mockedCity, "se9-wireguard", false) + + assertTrue(RelayNameComparator.compare(relay9a, relay9b) == 0) + assertTrue(RelayNameComparator.compare(relay9b, relay9a) == 0) + } + + @Test + fun test_compare_only_numbers_in_name() { + val relay001 = Relay(mockedCity, "001", false) + val relay1 = Relay(mockedCity, "1", false) + val relay3 = Relay(mockedCity, "3", false) + val relay100 = Relay(mockedCity, "100", false) + + relay001 assertOrderBothDirection relay1 + relay001 assertOrderBothDirection relay3 + relay1 assertOrderBothDirection relay3 + relay3 assertOrderBothDirection relay100 + } + + @Test + fun test_compare_without_numbers_in_name() { + val relay9a = Relay(mockedCity, "se-wireguard", false) + val relay9b = Relay(mockedCity, "se-wireguard", false) + + assertTrue(RelayNameComparator.compare(relay9a, relay9b) == 0) + assertTrue(RelayNameComparator.compare(relay9b, relay9a) == 0) + } + + @Test + fun test_compare_with_trailing_zeros_in_name() { + val relay001 = Relay(mockedCity, "se001-wireguard", false) + val relay005 = Relay(mockedCity, "se005-wireguard", false) + + relay001 assertOrderBothDirection relay005 + } + + @Test + fun test_compare_prefix_and_numbers() { + val relayAr2 = Relay(mockedCity, "ar2-wireguard", false) + val relayAr8 = Relay(mockedCity, "ar8-wireguard", false) + val relaySe5 = Relay(mockedCity, "se5-wireguard", false) + val relaySe10 = Relay(mockedCity, "se10-wireguard", false) + + relayAr2 assertOrderBothDirection relayAr8 + relayAr8 assertOrderBothDirection relaySe5 + relaySe5 assertOrderBothDirection relaySe10 + } + + @Test + fun test_compare_suffix_and_numbers() { + val relay2c = Relay(mockedCity, "se2-cloud", false) + val relay2w = Relay(mockedCity, "se2-wireguard", false) + + relay2c assertOrderBothDirection relay2w + } + + @Test + fun test_compare_different_length() { + val relay22a = Relay(mockedCity, "se22", false) + val relay22b = Relay(mockedCity, "se22-wireguard", false) + + relay22a assertOrderBothDirection relay22b + } + + private infix fun Relay.assertOrderBothDirection(other: Relay) { + assertTrue(RelayNameComparator.compare(this, other) < 0) + assertTrue(RelayNameComparator.compare(other, this) > 0) + } +} diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt new file mode 100644 index 0000000000..ac229ba3fb --- /dev/null +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt @@ -0,0 +1,238 @@ +package net.mullvad.mullvadvpn.viewmodel + +import androidx.annotation.StringRes +import androidx.lifecycle.viewModelScope +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.unmockkAll +import io.mockk.verify +import io.mockk.verifyAll +import java.util.concurrent.TimeUnit +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runBlockingTest +import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.TestCoroutineRule +import net.mullvad.mullvadvpn.applist.AppData +import net.mullvad.mullvadvpn.applist.ApplicationsProvider +import net.mullvad.mullvadvpn.applist.ViewIntent +import net.mullvad.mullvadvpn.assertLists +import net.mullvad.mullvadvpn.model.ListItemData +import net.mullvad.mullvadvpn.model.WidgetState +import net.mullvad.mullvadvpn.ui.serviceconnection.SplitTunneling +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.Timeout + +class SplitTunnelingViewModelTest { + @get:Rule + val testCoroutineRule = TestCoroutineRule() + + @get:Rule + val timeout = Timeout(3000L, TimeUnit.MILLISECONDS) + private val mockedApplicationsProvider = mockk<ApplicationsProvider>() + private val mockedSplitTunneling = mockk<SplitTunneling>() + private lateinit var testSubject: SplitTunnelingViewModel + + @Before + fun setup() { + every { mockedSplitTunneling.enabled } returns true + } + + @After + fun tearDown() { + testSubject.viewModelScope.coroutineContext.cancel() + unmockkAll() + } + + @Test + fun test_has_progress_on_start() = runBlockingTest(testCoroutineRule.testDispatcher) { + initTestSubject(emptyList()) + val actualList: List<ListItemData> = testSubject.listItems.first() + + val initialExpectedList = listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(0), + createProgressItem() + ) + + assertLists(initialExpectedList, actualList) + + verify(exactly = 1) { + mockedApplicationsProvider.getAppsList() + } + } + + @Test + fun test_empty_app_list() = runBlockingTest(testCoroutineRule.testDispatcher) { + initTestSubject(emptyList()) + testSubject.processIntent(ViewIntent.ViewIsReady) + val actualList = testSubject.listItems.first() + val expectedList = listOf(createTextItem(R.string.split_tunneling_description)) + assertLists(expectedList, actualList) + } + + @Test + fun test_apps_list_delivered() = runBlockingTest(testCoroutineRule.testDispatcher) { + val appExcluded = AppData("test.excluded", 0, "testName1") + val appNotExcluded = AppData("test.not.excluded", 0, "testName2") + every { mockedSplitTunneling.isAppExcluded(appExcluded.packageName) } returns true + every { mockedSplitTunneling.isAppExcluded(appNotExcluded.packageName) } returns false + + initTestSubject(listOf(appExcluded, appNotExcluded)) + testSubject.processIntent(ViewIntent.ViewIsReady) + + val actualList = testSubject.listItems.first() + val expectedList = listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(0), + createMainItem(R.string.exclude_applications), + createApplicationItem(appExcluded, true), + createDivider(1), + createSwitchItem(R.string.show_system_apps, false), + createMainItem(R.string.all_applications), + createApplicationItem(appNotExcluded, false), + ) + + assertLists(expectedList, actualList) + verifyAll { + mockedSplitTunneling.enabled + mockedSplitTunneling.isAppExcluded(appExcluded.packageName) + mockedSplitTunneling.isAppExcluded(appNotExcluded.packageName) + } + } + + @Test + fun test_remove_app_from_excluded() = runBlockingTest(testCoroutineRule.testDispatcher) { + val app = AppData("test", 0, "testName") + every { mockedSplitTunneling.isAppExcluded(app.packageName) } returns true + every { mockedSplitTunneling.includeApp(app.packageName) } just Runs + + initTestSubject(listOf(app)) + testSubject.processIntent(ViewIntent.ViewIsReady) + + val listBeforeAction = testSubject.listItems.first() + val expectedListBeforeAction = listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(0), + createMainItem(R.string.exclude_applications), + createApplicationItem(app, true), + ) + + assertLists(expectedListBeforeAction, listBeforeAction) + + val item = listBeforeAction.first { it.identifier == app.packageName } + testSubject.processIntent(ViewIntent.ChangeApplicationGroup(item)) + + val itemsAfterAction = testSubject.listItems.first() + val expectedList = listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(1), + createSwitchItem(R.string.show_system_apps, false), + createMainItem(R.string.all_applications), + createApplicationItem(app, false), + ) + + assertLists(expectedList, itemsAfterAction) + + verifyAll { + mockedSplitTunneling.enabled + mockedSplitTunneling.isAppExcluded(app.packageName) + mockedSplitTunneling.includeApp(app.packageName) + } + } + + @Test + fun test_add_app_to_excluded() = runBlockingTest(testCoroutineRule.testDispatcher) { + val app = AppData("test", 0, "testName") + every { mockedSplitTunneling.isAppExcluded(app.packageName) } returns false + every { mockedSplitTunneling.excludeApp(app.packageName) } just Runs + initTestSubject(listOf(app)) + testSubject.processIntent(ViewIntent.ViewIsReady) + + val listBeforeAction = testSubject.listItems.first() + val expectedListBeforeAction = listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(1), + createSwitchItem(R.string.show_system_apps, false), + createMainItem(R.string.all_applications), + createApplicationItem(app, false), + ) + + assertLists(expectedListBeforeAction, listBeforeAction) + + val item = listBeforeAction.first { it.identifier == app.packageName } + testSubject.processIntent(ViewIntent.ChangeApplicationGroup(item)) + + val itemsAfterAction = testSubject.listItems.first() + val expectedList = listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(0), + createMainItem(R.string.exclude_applications), + createApplicationItem(app, true), + ) + + assertLists(expectedList, itemsAfterAction) + + verifyAll { + mockedSplitTunneling.enabled + mockedSplitTunneling.isAppExcluded(app.packageName) + mockedSplitTunneling.excludeApp(app.packageName) + } + } + + private fun initTestSubject(appList: List<AppData>) { + every { mockedApplicationsProvider.getAppsList() } returns appList + testSubject = SplitTunnelingViewModel( + mockedApplicationsProvider, + mockedSplitTunneling, + testCoroutineRule.testDispatcher + ) + } + + private fun createApplicationItem( + appData: AppData, + checked: Boolean + ): ListItemData = ListItemData.build(appData.packageName) { + type = ListItemData.APPLICATION + text = appData.name + iconRes = appData.iconRes + action = ListItemData.ItemAction(appData.packageName) + widget = WidgetState.ImageState( + if (checked) R.drawable.ic_icons_remove else R.drawable.ic_icons_add + ) + } + + private fun createDivider(id: Int): ListItemData = ListItemData.build("space_$id") { + type = ListItemData.DIVIDER + } + + private fun createMainItem(@StringRes text: Int): ListItemData = + ListItemData.build("header_$text") { + type = ListItemData.ACTION + textRes = text + } + + private fun createTextItem(@StringRes text: Int): ListItemData = + ListItemData.build("text_$text") { + type = ListItemData.PLAIN + textRes = text + action = ListItemData.ItemAction(text.toString()) + } + + private fun createProgressItem(): ListItemData = ListItemData.build(identifier = "progress") { + type = ListItemData.PROGRESS + } + + private fun createSwitchItem(@StringRes text: Int, checked: Boolean): ListItemData = + ListItemData.build(identifier = "switch_$text") { + type = ListItemData.ACTION + textRes = text + action = ListItemData.ItemAction(text.toString()) + widget = WidgetState.SwitchState(checked) + } +} |
