diff options
7 files changed, 238 insertions, 160 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java index ef0f6cbc6ed9..13a06017abbc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java +++ b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java @@ -42,6 +42,8 @@ import androidx.annotation.Nullable; import com.android.settingslib.R; import com.android.settingslib.Utils; +import java.util.Objects; + /** * Drawable displaying a mobile cell signal indicator. */ @@ -90,6 +92,10 @@ public class SignalDrawable extends DrawableWrapper { private int mCurrentDot; public SignalDrawable(Context context) { + this(context, new Handler()); + } + + public SignalDrawable(@NonNull Context context, @NonNull Handler handler) { super(context.getDrawable(ICON_RES)); final String attributionPathString = context.getString( com.android.internal.R.string.config_signalAttributionPath); @@ -106,7 +112,7 @@ public class SignalDrawable extends DrawableWrapper { mIntrinsicSize = context.getResources().getDimensionPixelSize(R.dimen.signal_icon_size); mTransparentPaint.setColor(context.getColor(android.R.color.transparent)); mTransparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); - mHandler = new Handler(); + mHandler = handler; setDarkIntensity(0); } @@ -304,6 +310,17 @@ public class SignalDrawable extends DrawableWrapper { | level; } + @Override + public boolean equals(@Nullable Object other) { + return other instanceof SignalDrawable + && ((SignalDrawable) other).getLevel() == this.getLevel(); + } + + @Override + public int hashCode() { + return Objects.hash(getLevel()); + } + /** Returns the state representing empty mobile signal with the given number of levels. */ public static int getEmptyState(int numLevels) { return getState(0, numLevels, true); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt index 620e90dcaa62..d32ba47204c0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt @@ -17,13 +17,17 @@ package com.android.systemui.qs.tiles.impl.internet.domain import android.graphics.drawable.TestStubDrawable +import android.os.fakeExecutorHandler import android.widget.Switch import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.settingslib.graph.SignalDrawable import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription +import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text +import com.android.systemui.common.shared.model.Text.Companion.loadText import com.android.systemui.kosmos.Kosmos import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel @@ -31,6 +35,9 @@ import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileState import com.android.systemui.res.R import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS +import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel +import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel +import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel import org.junit.Test import org.junit.runner.RunWith @@ -39,25 +46,93 @@ import org.junit.runner.RunWith class InternetTileMapperTest : SysuiTestCase() { private val kosmos = Kosmos() private val internetTileConfig = kosmos.qsInternetTileConfig + private val handler = kosmos.fakeExecutorHandler private val mapper by lazy { InternetTileMapper( context.orCreateTestableResources .apply { addOverride(R.drawable.ic_qs_no_internet_unavailable, TestStubDrawable()) + addOverride(R.drawable.ic_satellite_connected_2, TestStubDrawable()) addOverride(wifiRes, TestStubDrawable()) } .resources, context.theme, - context + context, + handler, ) } @Test - fun withActiveModel_mappedStateMatchesDataModel() { + fun withActiveCellularModel_mappedStateMatchesDataModel() { val inputModel = InternetTileModel.Active( secondaryLabel = Text.Resource(R.string.quick_settings_networks_available), - iconId = wifiRes, + icon = InternetTileIconModel.Cellular(3), + stateDescription = null, + contentDescription = + ContentDescription.Resource(R.string.quick_settings_internet_label), + ) + + val outputState = mapper.map(internetTileConfig, inputModel) + + val signalDrawable = SignalDrawable(context, handler) + signalDrawable.setLevel(3) + val expectedState = + createInternetTileState( + QSTileState.ActivationState.ACTIVE, + context.getString(R.string.quick_settings_networks_available), + Icon.Loaded(signalDrawable, null), + null, + context.getString(R.string.quick_settings_internet_label), + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + @Test + fun withActiveSatelliteModel_mappedStateMatchesDataModel() { + val inputIcon = + SignalIconModel.Satellite( + 3, + Icon.Resource( + res = R.drawable.ic_satellite_connected_2, + contentDescription = + ContentDescription.Resource( + R.string.accessibility_status_bar_satellite_good_connection + ), + ), + ) + val inputModel = + InternetTileModel.Active( + secondaryLabel = Text.Resource(R.string.quick_settings_networks_available), + icon = InternetTileIconModel.Satellite(inputIcon.icon), + stateDescription = null, + contentDescription = + ContentDescription.Resource( + R.string.accessibility_status_bar_satellite_good_connection + ), + ) + + val outputState = mapper.map(internetTileConfig, inputModel) + + val expectedSatIcon = SatelliteIconModel.fromSignalStrength(3) + + val expectedState = + createInternetTileState( + QSTileState.ActivationState.ACTIVE, + inputModel.secondaryLabel.loadText(context).toString(), + Icon.Loaded(context.getDrawable(expectedSatIcon!!.res)!!, null), + expectedSatIcon.res, + expectedSatIcon.contentDescription.loadContentDescription(context).toString(), + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + @Test + fun withActiveWifiModel_mappedStateMatchesDataModel() { + val inputModel = + InternetTileModel.Active( + secondaryLabel = Text.Resource(R.string.quick_settings_networks_available), + icon = InternetTileIconModel.ResourceId(wifiRes), stateDescription = null, contentDescription = ContentDescription.Resource(R.string.quick_settings_internet_label), @@ -71,7 +146,7 @@ class InternetTileMapperTest : SysuiTestCase() { context.getString(R.string.quick_settings_networks_available), Icon.Loaded(context.getDrawable(wifiRes)!!, contentDescription = null), wifiRes, - context.getString(R.string.quick_settings_internet_label) + context.getString(R.string.quick_settings_internet_label), ) QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) } @@ -81,7 +156,7 @@ class InternetTileMapperTest : SysuiTestCase() { val inputModel = InternetTileModel.Inactive( secondaryLabel = Text.Resource(R.string.quick_settings_networks_unavailable), - iconId = R.drawable.ic_qs_no_internet_unavailable, + icon = InternetTileIconModel.ResourceId(R.drawable.ic_qs_no_internet_unavailable), stateDescription = null, contentDescription = ContentDescription.Resource(R.string.quick_settings_networks_unavailable), @@ -95,10 +170,10 @@ class InternetTileMapperTest : SysuiTestCase() { context.getString(R.string.quick_settings_networks_unavailable), Icon.Loaded( context.getDrawable(R.drawable.ic_qs_no_internet_unavailable)!!, - contentDescription = null + contentDescription = null, ), R.drawable.ic_qs_no_internet_unavailable, - context.getString(R.string.quick_settings_networks_unavailable) + context.getString(R.string.quick_settings_networks_unavailable), ) QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) } @@ -107,7 +182,7 @@ class InternetTileMapperTest : SysuiTestCase() { activationState: QSTileState.ActivationState, secondaryLabel: String, icon: Icon, - iconRes: Int, + iconRes: Int? = null, contentDescription: String, ): QSTileState { val label = context.getString(R.string.quick_settings_internet_label) @@ -120,13 +195,13 @@ class InternetTileMapperTest : SysuiTestCase() { setOf( QSTileState.UserAction.CLICK, QSTileState.UserAction.TOGGLE_CLICK, - QSTileState.UserAction.LONG_CLICK + QSTileState.UserAction.LONG_CLICK, ), contentDescription, null, QSTileState.SideViewIcon.Chevron, QSTileState.EnabledState.ENABLED, - Switch::class.qualifiedName + Switch::class.qualifiedName, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt index 5a4506086058..5259aa84b193 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt @@ -18,14 +18,12 @@ package com.android.systemui.qs.tiles.impl.internet.domain.interactor import android.graphics.drawable.TestStubDrawable import android.os.UserHandle -import android.testing.TestableLooper 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.ContentDescription.Companion.loadContentDescription -import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.common.shared.model.Text.Companion.loadText import com.android.systemui.coroutines.collectLastValue @@ -49,6 +47,7 @@ import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIc import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository +import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel @@ -60,9 +59,7 @@ import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest -import org.junit.Assume.assumeFalse import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -144,7 +141,6 @@ class InternetTileDataInteractorTest : SysuiTestCase() { underTest = InternetTileDataInteractor( context, - testScope.coroutineContext, testScope.backgroundScope, airplaneModeRepository, connectivityRepository, @@ -164,9 +160,11 @@ class InternetTileDataInteractorTest : SysuiTestCase() { connectivityRepository.defaultConnections.value = DefaultConnectionModel() + val expectedIcon = + InternetTileIconModel.ResourceId(R.drawable.ic_qs_no_internet_unavailable) assertThat(latest?.secondaryLabel) .isEqualTo(Text.Resource(R.string.quick_settings_networks_unavailable)) - assertThat(latest?.iconId).isEqualTo(R.drawable.ic_qs_no_internet_unavailable) + assertThat(latest?.icon).isEqualTo(expectedIcon) } @Test @@ -183,11 +181,8 @@ class InternetTileDataInteractorTest : SysuiTestCase() { underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) ) - val networkModel = - WifiNetworkModel.Active.of( - level = 4, - ssid = "test ssid", - ) + val networkModel = WifiNetworkModel.Active.of(level = 4, ssid = "test ssid") + val wifiIcon = WifiIcon.fromModel(model = networkModel, context = context, showHotspotInfo = true) as WifiIcon.Visible @@ -198,12 +193,9 @@ class InternetTileDataInteractorTest : SysuiTestCase() { assertThat(latest?.secondaryTitle).isEqualTo("test ssid") assertThat(latest?.secondaryLabel).isNull() - val expectedIcon = - Icon.Loaded(context.getDrawable(WifiIcons.WIFI_NO_INTERNET_ICONS[4])!!, null) - val actualIcon = latest?.icon - assertThat(actualIcon).isEqualTo(expectedIcon) - assertThat(latest?.iconId).isEqualTo(WifiIcons.WIFI_NO_INTERNET_ICONS[4]) + val expectedIcon = InternetTileIconModel.ResourceId(WifiIcons.WIFI_NO_INTERNET_ICONS[4]) + assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.contentDescription.loadContentDescription(context)) .isEqualTo("$internet,test ssid") val expectedSd = wifiIcon.contentDescription @@ -229,8 +221,7 @@ class InternetTileDataInteractorTest : SysuiTestCase() { wifiRepository.setIsWifiDefault(true) wifiRepository.setWifiNetwork(networkModel) - val expectedIcon = - Icon.Loaded(context.getDrawable(WifiIcons.WIFI_NO_INTERNET_ICONS[4])!!, null) + val expectedIcon = InternetTileIconModel.ResourceId(WifiIcons.WIFI_NO_INTERNET_ICONS[4]) assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription.loadContentDescription(context)) .doesNotContain( @@ -249,9 +240,8 @@ class InternetTileDataInteractorTest : SysuiTestCase() { setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.TABLET) val expectedIcon = - Icon.Loaded( - context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_tablet)!!, - null + InternetTileIconModel.ResourceId( + com.android.settingslib.R.drawable.ic_hotspot_tablet ) assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription.loadContentDescription(context)) @@ -271,9 +261,8 @@ class InternetTileDataInteractorTest : SysuiTestCase() { setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.LAPTOP) val expectedIcon = - Icon.Loaded( - context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_laptop)!!, - null + InternetTileIconModel.ResourceId( + com.android.settingslib.R.drawable.ic_hotspot_laptop ) assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription.loadContentDescription(context)) @@ -293,10 +282,10 @@ class InternetTileDataInteractorTest : SysuiTestCase() { setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.WATCH) val expectedIcon = - Icon.Loaded( - context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_watch)!!, - null + InternetTileIconModel.ResourceId( + com.android.settingslib.R.drawable.ic_hotspot_watch ) + assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription.loadContentDescription(context)) .isEqualTo( @@ -315,10 +304,7 @@ class InternetTileDataInteractorTest : SysuiTestCase() { setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.AUTO) val expectedIcon = - Icon.Loaded( - context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_auto)!!, - null - ) + InternetTileIconModel.ResourceId(com.android.settingslib.R.drawable.ic_hotspot_auto) assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription.loadContentDescription(context)) .isEqualTo( @@ -336,9 +322,8 @@ class InternetTileDataInteractorTest : SysuiTestCase() { setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.PHONE) val expectedIcon = - Icon.Loaded( - context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_phone)!!, - null + InternetTileIconModel.ResourceId( + com.android.settingslib.R.drawable.ic_hotspot_phone ) assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription.loadContentDescription(context)) @@ -358,9 +343,8 @@ class InternetTileDataInteractorTest : SysuiTestCase() { setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.UNKNOWN) val expectedIcon = - Icon.Loaded( - context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_phone)!!, - null + InternetTileIconModel.ResourceId( + com.android.settingslib.R.drawable.ic_hotspot_phone ) assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription.loadContentDescription(context)) @@ -380,10 +364,10 @@ class InternetTileDataInteractorTest : SysuiTestCase() { setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.INVALID) val expectedIcon = - Icon.Loaded( - context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_phone)!!, - null + InternetTileIconModel.ResourceId( + com.android.settingslib.R.drawable.ic_hotspot_phone ) + assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription.loadContentDescription(context)) .isEqualTo( @@ -426,8 +410,9 @@ class InternetTileDataInteractorTest : SysuiTestCase() { assertThat(latest?.secondaryLabel).isNull() assertThat(latest?.secondaryTitle) .isEqualTo(context.getString(R.string.quick_settings_networks_available)) - assertThat(latest?.icon).isNull() - assertThat(latest?.iconId).isEqualTo(R.drawable.ic_qs_no_internet_available) + val expectedIcon = + InternetTileIconModel.ResourceId(R.drawable.ic_qs_no_internet_available) + assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription).isNull() val expectedCd = "$internet,${context.getString(R.string.quick_settings_networks_available)}" @@ -435,54 +420,19 @@ class InternetTileDataInteractorTest : SysuiTestCase() { .isEqualTo(expectedCd) } - /** - * We expect a RuntimeException because [underTest] instantiates a SignalDrawable on the - * provided context, and so the SignalDrawable constructor attempts to instantiate a Handler() - * on the mentioned context. Since that context does not have a looper assigned to it, the - * handler instantiation will throw a RuntimeException. - * - * TODO(b/338068066): Robolectric behavior differs in that it does not throw the exception So - * either we should make Robolectric behave similar to the device test, or change this test to - * look for a different signal than the exception, when run by Robolectric. For now we just - * assume the test is not Robolectric. - */ - @Test(expected = java.lang.RuntimeException::class) - fun mobileDefault_usesNetworkNameAndIcon_throwsRunTimeException() = - testScope.runTest { - assumeFalse(isRobolectricTest()) - - collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))) - - connectivityRepository.setMobileConnected() - mobileConnectionsRepository.mobileIsDefault.value = true - mobileConnectionRepository.apply { - setAllLevels(3) - setAllRoaming(false) - networkName.value = NetworkNameModel.Default("test network") - } - - runCurrent() - } - - /** - * See [mobileDefault_usesNetworkNameAndIcon_throwsRunTimeException] for description of the - * problem this test solves. The solution here is to assign a looper to the context via - * RunWithLooper. In the production code, the solution is to use a Main CoroutineContext for - * creating the SignalDrawable. - */ - @TestableLooper.RunWithLooper @Test - fun mobileDefault_run_withLooper_usesNetworkNameAndIcon() = + fun mobileDefault_usesNetworkNameAndIcon() = testScope.runTest { val latest by collectLastValue( underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) ) + val iconLevel = 3 connectivityRepository.setMobileConnected() mobileConnectionsRepository.mobileIsDefault.value = true mobileConnectionRepository.apply { - setAllLevels(3) + setAllLevels(iconLevel) setAllRoaming(false) networkName.value = NetworkNameModel.Default("test network") } @@ -491,8 +441,9 @@ class InternetTileDataInteractorTest : SysuiTestCase() { assertThat(latest?.secondaryTitle).isNotNull() assertThat(latest?.secondaryTitle.toString()).contains("test network") assertThat(latest?.secondaryLabel).isNull() - assertThat(latest?.icon).isInstanceOf(Icon.Loaded::class.java) - assertThat(latest?.iconId).isNull() + val expectedIcon = InternetTileIconModel.Cellular(iconLevel) + + assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription.loadContentDescription(context)) .isEqualTo(latest?.secondaryTitle.toString()) assertThat(latest?.contentDescription.loadContentDescription(context)) @@ -513,8 +464,8 @@ class InternetTileDataInteractorTest : SysuiTestCase() { assertThat(latest?.secondaryLabel.loadText(context)) .isEqualTo(ethernetIcon!!.contentDescription.loadContentDescription(context)) assertThat(latest?.secondaryTitle).isNull() - assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet_fully) - assertThat(latest?.icon).isNull() + val expectedIcon = InternetTileIconModel.ResourceId(R.drawable.stat_sys_ethernet_fully) + assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription).isNull() assertThat(latest?.contentDescription.loadContentDescription(context)) .isEqualTo(latest?.secondaryLabel.loadText(context)) @@ -534,8 +485,8 @@ class InternetTileDataInteractorTest : SysuiTestCase() { assertThat(latest?.secondaryLabel.loadText(context)) .isEqualTo(ethernetIcon!!.contentDescription.loadContentDescription(context)) assertThat(latest?.secondaryTitle).isNull() - assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet) - assertThat(latest?.icon).isNull() + val expectedIcon = InternetTileIconModel.ResourceId(R.drawable.stat_sys_ethernet) + assertThat(latest?.icon).isEqualTo(expectedIcon) assertThat(latest?.stateDescription).isNull() assertThat(latest?.contentDescription.loadContentDescription(context)) .isEqualTo(latest?.secondaryLabel.loadText(context)) @@ -543,11 +494,7 @@ class InternetTileDataInteractorTest : SysuiTestCase() { private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) { val networkModel = - WifiNetworkModel.Active.of( - level = 4, - ssid = "test ssid", - hotspotDeviceType = hotspot, - ) + WifiNetworkModel.Active.of(level = 4, ssid = "test ssid", hotspotDeviceType = hotspot) connectivityRepository.setWifiConnected() wifiRepository.setIsWifiDefault(true) @@ -560,7 +507,7 @@ class InternetTileDataInteractorTest : SysuiTestCase() { val NOT_CONNECTED_NETWORKS_UNAVAILABLE = InternetTileModel.Inactive( secondaryLabel = Text.Resource(R.string.quick_settings_networks_unavailable), - iconId = R.drawable.ic_qs_no_internet_unavailable, + icon = InternetTileIconModel.ResourceId(R.drawable.ic_qs_no_internet_unavailable), stateDescription = null, contentDescription = ContentDescription.Resource(R.string.quick_settings_networks_unavailable), diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt index 8965ef2bc493..bb0b9b7084fa 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt @@ -18,7 +18,9 @@ package com.android.systemui.qs.tiles.impl.internet.domain import android.content.Context import android.content.res.Resources +import android.os.Handler import android.widget.Switch +import com.android.settingslib.graph.SignalDrawable import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text.Companion.loadText @@ -28,6 +30,7 @@ import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileMode import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileState import com.android.systemui.res.R +import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel import javax.inject.Inject /** Maps [InternetTileModel] to [QSTileState]. */ @@ -37,6 +40,7 @@ constructor( @Main private val resources: Resources, private val theme: Resources.Theme, private val context: Context, + @Main private val handler: Handler, ) : QSTileDataToStateMapper<InternetTileModel> { override fun map(config: QSTileConfig, data: InternetTileModel): QSTileState = @@ -44,25 +48,42 @@ constructor( label = resources.getString(R.string.quick_settings_internet_label) expandedAccessibilityClass = Switch::class - if (data.secondaryLabel != null) { - secondaryLabel = data.secondaryLabel.loadText(context) - } else { - secondaryLabel = data.secondaryTitle - } + secondaryLabel = + if (data.secondaryLabel != null) { + data.secondaryLabel.loadText(context) + } else { + data.secondaryTitle + } stateDescription = data.stateDescription.loadContentDescription(context) contentDescription = data.contentDescription.loadContentDescription(context) - iconRes = data.iconId - if (data.icon != null) { - this.icon = { data.icon } - } else if (data.iconId != null) { - val loadedIcon = - Icon.Loaded( - resources.getDrawable(data.iconId!!, theme), - contentDescription = null - ) - this.icon = { loadedIcon } + when (val dataIcon = data.icon) { + is InternetTileIconModel.ResourceId -> { + iconRes = dataIcon.resId + icon = { + Icon.Loaded( + resources.getDrawable(dataIcon.resId, theme), + contentDescription = null, + ) + } + } + + is InternetTileIconModel.Cellular -> { + val signalDrawable = SignalDrawable(context, handler) + signalDrawable.setLevel(dataIcon.level) + icon = { Icon.Loaded(signalDrawable, contentDescription = null) } + } + + is InternetTileIconModel.Satellite -> { + iconRes = dataIcon.resourceIcon.res // level is inferred from res + icon = { + Icon.Loaded( + resources.getDrawable(dataIcon.resourceIcon.res, theme), + contentDescription = null, + ) + } + } } sideViewIcon = QSTileState.SideViewIcon.Chevron @@ -75,7 +96,7 @@ constructor( setOf( QSTileState.UserAction.CLICK, QSTileState.UserAction.TOGGLE_CLICK, - QSTileState.UserAction.LONG_CLICK + QSTileState.UserAction.LONG_CLICK, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt index 204ead3fe29c..6fe3979fa446 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt @@ -20,13 +20,10 @@ import android.annotation.StringRes import android.content.Context import android.os.UserHandle import android.text.Html -import com.android.settingslib.graph.SignalDrawable import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription -import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel @@ -36,12 +33,12 @@ import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteracto import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository +import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon import com.android.systemui.utils.coroutines.flow.mapLatestConflated import javax.inject.Inject -import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -51,7 +48,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.withContext @OptIn(ExperimentalCoroutinesApi::class) /** Observes internet state changes providing the [InternetTileModel]. */ @@ -59,7 +55,6 @@ class InternetTileDataInteractor @Inject constructor( private val context: Context, - @Main private val mainCoroutineContext: CoroutineContext, @Application private val scope: CoroutineScope, airplaneModeRepository: AirplaneModeRepository, private val connectivityRepository: ConnectivityRepository, @@ -79,8 +74,7 @@ constructor( flowOf( InternetTileModel.Active( secondaryTitle = secondary, - iconId = wifiIcon.icon.res, - icon = Icon.Loaded(context.getDrawable(wifiIcon.icon.res)!!, null), + icon = InternetTileIconModel.ResourceId(wifiIcon.icon.res), stateDescription = wifiIcon.contentDescription, contentDescription = ContentDescription.Loaded("$internetLabel,$secondary"), ) @@ -116,11 +110,10 @@ constructor( if (it == null) { notConnectedFlow } else { - combine( - it.networkName, - it.signalLevelIcon, - mobileDataContentName, - ) { networkNameModel, signalIcon, dataContentDescription -> + combine(it.networkName, it.signalLevelIcon, mobileDataContentName) { + networkNameModel, + signalIcon, + dataContentDescription -> Triple(networkNameModel, signalIcon, dataContentDescription) } .mapLatestConflated { (networkNameModel, signalIcon, dataContentDescription) -> @@ -129,17 +122,12 @@ constructor( val secondary = mobileDataContentConcat( networkNameModel.name, - dataContentDescription + dataContentDescription, ) - val drawable = - withContext(mainCoroutineContext) { SignalDrawable(context) } - drawable.setLevel(signalIcon.level) - val loadedIcon = Icon.Loaded(drawable, null) - InternetTileModel.Active( secondaryTitle = secondary, - icon = loadedIcon, + icon = InternetTileIconModel.Cellular(signalIcon.level), stateDescription = ContentDescription.Loaded(secondary.toString()), contentDescription = ContentDescription.Loaded(internetLabel), @@ -150,9 +138,10 @@ constructor( signalIcon.icon.contentDescription.loadContentDescription( context ) + InternetTileModel.Active( secondaryTitle = secondary, - iconId = signalIcon.icon.res, + icon = InternetTileIconModel.Satellite(signalIcon.icon), stateDescription = ContentDescription.Loaded(secondary), contentDescription = ContentDescription.Loaded(internetLabel), ) @@ -164,7 +153,7 @@ constructor( private fun mobileDataContentConcat( networkName: String?, - dataContentDescription: CharSequence? + dataContentDescription: CharSequence?, ): CharSequence { if (dataContentDescription == null) { return networkName ?: "" @@ -177,9 +166,9 @@ constructor( context.getString( R.string.mobile_carrier_text_format, networkName, - dataContentDescription + dataContentDescription, ), - 0 + 0, ) } @@ -199,7 +188,7 @@ constructor( flowOf( InternetTileModel.Active( secondaryLabel = secondary?.toText(), - iconId = it.res, + icon = InternetTileIconModel.ResourceId(it.res), stateDescription = null, contentDescription = secondary, ) @@ -208,16 +197,18 @@ constructor( } private val notConnectedFlow: StateFlow<InternetTileModel> = - combine( - wifiInteractor.areNetworksAvailable, - airplaneModeRepository.isAirplaneMode, - ) { networksAvailable, isAirplaneMode -> + combine(wifiInteractor.areNetworksAvailable, airplaneModeRepository.isAirplaneMode) { + networksAvailable, + isAirplaneMode -> when { isAirplaneMode -> { val secondary = context.getString(R.string.status_bar_airplane) InternetTileModel.Inactive( secondaryTitle = secondary, - iconId = R.drawable.ic_qs_no_internet_unavailable, + icon = + InternetTileIconModel.ResourceId( + R.drawable.ic_qs_no_internet_unavailable + ), stateDescription = null, contentDescription = ContentDescription.Loaded(secondary), ) @@ -227,10 +218,13 @@ constructor( context.getString(R.string.quick_settings_networks_available) InternetTileModel.Inactive( secondaryTitle = secondary, - iconId = R.drawable.ic_qs_no_internet_available, + icon = + InternetTileIconModel.ResourceId( + R.drawable.ic_qs_no_internet_available + ), stateDescription = null, contentDescription = - ContentDescription.Loaded("$internetLabel,$secondary") + ContentDescription.Loaded("$internetLabel,$secondary"), ) } else -> { @@ -248,7 +242,7 @@ constructor( */ override fun tileData( user: UserHandle, - triggers: Flow<DataUpdateTrigger> + triggers: Flow<DataUpdateTrigger>, ): Flow<InternetTileModel> = connectivityRepository.defaultConnections.flatMapLatest { when { @@ -265,7 +259,7 @@ constructor( val NOT_CONNECTED_NETWORKS_UNAVAILABLE = InternetTileModel.Inactive( secondaryLabel = Text.Resource(R.string.quick_settings_networks_unavailable), - iconId = R.drawable.ic_qs_no_internet_unavailable, + icon = InternetTileIconModel.ResourceId(R.drawable.ic_qs_no_internet_unavailable), stateDescription = null, contentDescription = ContentDescription.Resource(R.string.quick_settings_networks_unavailable), diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/model/InternetTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/model/InternetTileModel.kt index ece904611782..15b4e472eec7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/model/InternetTileModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/model/InternetTileModel.kt @@ -17,23 +17,21 @@ package com.android.systemui.qs.tiles.impl.internet.domain.model import com.android.systemui.common.shared.model.ContentDescription -import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text +import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel /** Model describing the state that the QS Internet tile should be in. */ sealed interface InternetTileModel { val secondaryTitle: CharSequence? val secondaryLabel: Text? - val iconId: Int? - val icon: Icon? + val icon: InternetTileIconModel val stateDescription: ContentDescription? val contentDescription: ContentDescription? data class Active( override val secondaryTitle: CharSequence? = null, override val secondaryLabel: Text? = null, - override val iconId: Int? = null, - override val icon: Icon? = null, + override val icon: InternetTileIconModel = InternetTileIconModel.Cellular(1), override val stateDescription: ContentDescription? = null, override val contentDescription: ContentDescription? = null, ) : InternetTileModel @@ -41,8 +39,7 @@ sealed interface InternetTileModel { data class Inactive( override val secondaryTitle: CharSequence? = null, override val secondaryLabel: Text? = null, - override val iconId: Int? = null, - override val icon: Icon? = null, + override val icon: InternetTileIconModel = InternetTileIconModel.Cellular(1), override val stateDescription: ContentDescription? = null, override val contentDescription: ContentDescription? = null, ) : InternetTileModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileIconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileIconModel.kt new file mode 100644 index 000000000000..f8958e0d002f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileIconModel.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.pipeline.shared.ui.model + +import com.android.systemui.common.shared.model.Icon + +sealed interface InternetTileIconModel { + data class ResourceId(val resId: Int) : InternetTileIconModel + + data class Cellular(val level: Int) : InternetTileIconModel + + data class Satellite(val resourceIcon: Icon.Resource) : InternetTileIconModel +} |