Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit db6f9c04 authored by Ahmed Mehfooz's avatar Ahmed Mehfooz Committed by Android (Google) Code Review
Browse files

Merge "[SB][ComposeIcons] Add Ethernet icon" into main

parents 475c7ab2 4123faaa
Loading
Loading
Loading
Loading
+144 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
import com.android.systemui.statusbar.pipeline.shared.data.repository.fake
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class EthernetIconViewModelTest : SysuiTestCase() {

    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val fakeConnectivityRepository: FakeConnectivityRepository by lazy {
        kosmos.connectivityRepository.fake
    }

    private val underTest = kosmos.ethernetIconViewModelFactory.create()

    @Before
    fun setUp() {
        underTest.activateIn(kosmos.testScope)
    }

    @Test
    fun icon_default_validated() =
        kosmos.runTest {
            // Ethernet is the default connection and validated
            fakeConnectivityRepository.setEthernetConnected(default = true, validated = true)

            // Icon is the 'fully connected' ethernet icon
            val expected =
                Icon.Resource(
                    R.drawable.stat_sys_ethernet_fully,
                    ContentDescription.Resource(
                        AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[1]
                    ),
                )

            assertThat(underTest.icon).isEqualTo(expected)
        }

    @Test
    fun icon_default_notValidated() =
        kosmos.runTest {
            // Ethernet is the default connection but not validated
            fakeConnectivityRepository.setEthernetConnected(default = true, validated = false)

            // Icon is the 'connected, not validated' ethernet icon
            val expected =
                Icon.Resource(
                    R.drawable.stat_sys_ethernet,
                    ContentDescription.Resource(
                        AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[0]
                    ),
                )

            assertThat(underTest.icon).isEqualTo(expected)
        }

    @Test
    fun icon_notDefault_validated() =
        kosmos.runTest {
            // Ethernet is connected and validated, but NOT the default connection
            fakeConnectivityRepository.setEthernetConnected(default = false, validated = true)

            // Icon is null (we only show the icon for the default connection)
            assertThat(underTest.icon).isNull()
        }

    @Test
    fun icon_notDefault_notValidated() =
        kosmos.runTest {
            // Ethernet is connected, but NOT validated and NOT the default connection
            fakeConnectivityRepository.setEthernetConnected(default = false, validated = false)

            // Icon is null
            assertThat(underTest.icon).isNull()
        }

    @Test
    fun icon_updatesWhenConnectivityChanges() =
        kosmos.runTest {
            // Start default and validated
            fakeConnectivityRepository.setEthernetConnected(default = true, validated = true)
            val expectedFully =
                Icon.Resource(
                    R.drawable.stat_sys_ethernet_fully,
                    ContentDescription.Resource(
                        AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[1]
                    ),
                )
            assertThat(underTest.icon).isEqualTo(expectedFully)

            // Change to default but not validated
            fakeConnectivityRepository.setEthernetConnected(default = true, validated = false)
            val expectedNotValidated =
                Icon.Resource(
                    R.drawable.stat_sys_ethernet,
                    ContentDescription.Resource(
                        AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[0]
                    ),
                )
            assertThat(underTest.icon).isEqualTo(expectedNotValidated)

            // Change to not default
            fakeConnectivityRepository.setEthernetConnected(default = false, validated = false)
            assertThat(underTest.icon).isNull()

            // Change back to default and validated
            fakeConnectivityRepository.setEthernetConnected(default = true, validated = true)
            assertThat(underTest.icon).isEqualTo(expectedFully)
        }
}
+53 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel

import androidx.compose.runtime.getValue
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
import com.android.systemui.statusbar.systemstatusicons.SystemStatusIconsInCompose
import com.android.systemui.statusbar.systemstatusicons.ui.viewmodel.SystemStatusIconViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

/**
 * View model for the ethernet system status icon. Emits an icon when ethernet is connected and the
 * default connection. Null icon otherwise.
 */
class EthernetIconViewModel @AssistedInject constructor(interactor: EthernetInteractor) :
    SystemStatusIconViewModel, ExclusiveActivatable() {

    init {
        /* check if */ SystemStatusIconsInCompose.isUnexpectedlyInLegacyMode()
    }

    private val hydrator = Hydrator("EthernetIconViewModel.hydrator")

    override val icon: Icon? by
        hydrator.hydratedStateOf(traceName = null, initialValue = null, source = interactor.icon)

    override suspend fun onActivated(): Nothing {
        hydrator.activate()
    }

    @AssistedFactory
    interface Factory {
        fun create(): EthernetIconViewModel
    }
}
+17 −4
Original line number Diff line number Diff line
@@ -20,8 +20,12 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.statusbar.systemstatusicons.SystemStatusIconsInCompose
import com.android.systemui.statusbar.systemstatusicons.airplane.ui.viewmodel.AirplaneModeIconViewModel
import com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel.EthernetIconViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch

/**
 * ViewModel for managing and displaying a list of system status icons.
@@ -31,21 +35,30 @@ import dagger.assisted.AssistedInject
 */
class SystemStatusIconsViewModel
@AssistedInject
constructor(airplaneModeIconViewModelFactory: AirplaneModeIconViewModel.Factory) :
    ExclusiveActivatable() {
constructor(
    airplaneModeIconViewModelFactory: AirplaneModeIconViewModel.Factory,
    ethernetIconViewModelFactory: EthernetIconViewModel.Factory,
) : ExclusiveActivatable() {

    init {
        /* check if */ SystemStatusIconsInCompose.isUnexpectedlyInLegacyMode()
    }

    private val airplaneModeIcon by lazy { airplaneModeIconViewModelFactory.create() }
    private val iconViewModels by lazy { listOf(airplaneModeIcon) }
    private val ethernetIcon by lazy { ethernetIconViewModelFactory.create() }
    private val iconViewModels: List<SystemStatusIconViewModel> by lazy {
        listOf(ethernetIcon, airplaneModeIcon)
    }

    val icons: List<Icon>
        get() = iconViewModels.mapNotNull { viewModel -> viewModel.icon }

    override suspend fun onActivated(): Nothing {
        airplaneModeIcon.activate()
        coroutineScope {
            launch { ethernetIcon.activate() }
            launch { airplaneModeIcon.activate() }
        }
        awaitCancellation()
    }

    @AssistedFactory
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel

import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.pipeline.ethernet.domain.ethernetInteractor

private val Kosmos.ethernetIconViewModel: EthernetIconViewModel by
    Kosmos.Fixture { EthernetIconViewModel(ethernetInteractor) }

val Kosmos.ethernetIconViewModelFactory: EthernetIconViewModel.Factory by
    Kosmos.Fixture {
        object : EthernetIconViewModel.Factory {
            override fun create(): EthernetIconViewModel = ethernetIconViewModel
        }
    }
+3 −1
Original line number Diff line number Diff line
@@ -18,11 +18,13 @@ package com.android.systemui.statusbar.systemstatusicons.ui.viewmodel

import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.systemstatusicons.airplane.ui.viewmodel.airplaneModeIconViewModelFactory
import com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel.ethernetIconViewModelFactory

val Kosmos.systemStatusIconsViewModel by
    Kosmos.Fixture {
        SystemStatusIconsViewModel(
            airplaneModeIconViewModelFactory = airplaneModeIconViewModelFactory
            airplaneModeIconViewModelFactory = airplaneModeIconViewModelFactory,
            ethernetIconViewModelFactory = ethernetIconViewModelFactory,
        )
    }