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

Commit 367616f2 authored by Evan Laird's avatar Evan Laird Committed by Android (Google) Code Review
Browse files

Merge "[mobile] use networkName in content description" into main

parents ad218039 dae24def
Loading
Loading
Loading
Loading
+124 −22
Original line number Diff line number Diff line
@@ -20,8 +20,6 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.TelephonyIcons.G
import com.android.settingslib.mobile.TelephonyIcons.THREE_G
@@ -40,12 +38,15 @@ import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesF
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository.Companion.DEFAULT_NETWORK_NAME
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -255,59 +256,146 @@ class MobileIconViewModelTest : SysuiTestCase() {
    @Test
    fun contentDescription_notInService_usesNoPhone() =
        testScope.runTest {
            var latest: ContentDescription? = null
            val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
            val latest by collectLastValue(underTest.contentDescription)

            repository.isInService.value = false

            assertThat((latest as ContentDescription.Resource).res)
                .isEqualTo(PHONE_SIGNAL_STRENGTH_NONE)
            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
        }

            job.cancel()
    @Test
    fun contentDescription_includesNetworkName() =
        testScope.runTest {
            val latest by collectLastValue(underTest.contentDescription)

            repository.isInService.value = true
            repository.networkName.value = NetworkNameModel.SubscriptionDerived("Test Network Name")
            repository.numberOfLevels.value = 5
            repository.setAllLevels(3)

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular("Test Network Name", THREE_BARS))
        }

    @Test
    fun contentDescription_inService_usesLevel() =
        testScope.runTest {
            var latest: ContentDescription? = null
            val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
            val latest by collectLastValue(underTest.contentDescription)

            repository.setAllLevels(2)
            assertThat((latest as ContentDescription.Resource).res)
                .isEqualTo(PHONE_SIGNAL_STRENGTH[2])

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))

            repository.setAllLevels(0)
            assertThat((latest as ContentDescription.Resource).res)
                .isEqualTo(PHONE_SIGNAL_STRENGTH[0])

            job.cancel()
            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
        }

    @Test
    fun contentDescription_nonInflated_invalidLevelIsNull() =
    fun contentDescription_nonInflated_invalidLevelUsesNoSignalText() =
        testScope.runTest {
            val latest by collectLastValue(underTest.contentDescription)

            repository.inflateSignalStrength.value = false
            repository.setAllLevels(-1)
            assertThat(latest).isNull()

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))

            repository.setAllLevels(100)
            assertThat(latest).isNull()

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
        }

    @Test
    fun contentDescription_nonInflated_levelStrings() =
        testScope.runTest {
            val latest by collectLastValue(underTest.contentDescription)

            repository.inflateSignalStrength.value = false
            repository.setAllLevels(0)

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))

            repository.setAllLevels(1)

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR))

            repository.setAllLevels(2)

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))

            repository.setAllLevels(3)

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS))

            repository.setAllLevels(4)

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS))
        }

    @Test
    fun contentDescription_inflated_invalidLevelIsNull() =
    fun contentDescription_inflated_invalidLevelUsesNoSignalText() =
        testScope.runTest {
            val latest by collectLastValue(underTest.contentDescription)

            repository.inflateSignalStrength.value = true
            repository.numberOfLevels.value = 6

            repository.setAllLevels(-2)
            assertThat(latest).isNull()

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))

            repository.setAllLevels(100)
            assertThat(latest).isNull()

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
        }

    @Test
    fun contentDescription_inflated_levelStrings() =
        testScope.runTest {
            val latest by collectLastValue(underTest.contentDescription)

            repository.inflateSignalStrength.value = true
            repository.numberOfLevels.value = 6

            // Note that the _repo_ level is 1 lower than the reported level through the interactor

            repository.setAllLevels(0)

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR))

            repository.setAllLevels(1)

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))

            repository.setAllLevels(2)

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS))

            repository.setAllLevels(3)

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FOUR_BARS))

            repository.setAllLevels(4)

            assertThat(latest as MobileContentDescription.Cellular)
                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS))
        }

    @Test
@@ -323,7 +411,10 @@ class MobileIconViewModelTest : SysuiTestCase() {
                repository.setAllLevels(i)
                when (i) {
                    -1,
                    5 -> assertWithMessage("Level $i is expected to be null").that(latest).isNull()
                    5 ->
                        assertWithMessage("Level $i is expected to be 'no signal'")
                            .that((latest as MobileContentDescription.Cellular).levelDescriptionRes)
                            .isEqualTo(NO_SIGNAL)
                    else ->
                        assertWithMessage("Level $i is expected not to be null")
                            .that(latest)
@@ -344,7 +435,10 @@ class MobileIconViewModelTest : SysuiTestCase() {
                repository.setAllLevels(i)
                when (i) {
                    -2,
                    5 -> assertWithMessage("Level $i is expected to be null").that(latest).isNull()
                    5 ->
                        assertWithMessage("Level $i is expected to be 'no signal'")
                            .that((latest as MobileContentDescription.Cellular).levelDescriptionRes)
                            .isEqualTo(NO_SIGNAL)
                    else ->
                        assertWithMessage("Level $i is not expected to be null")
                            .that(latest)
@@ -967,5 +1061,13 @@ class MobileIconViewModelTest : SysuiTestCase() {

    companion object {
        private const val SUB_1_ID = 1

        // For convenience, just define these as constants
        private val NO_SIGNAL = R.string.accessibility_no_signal
        private val ONE_BAR = R.string.accessibility_one_bar
        private val TWO_BARS = R.string.accessibility_two_bars
        private val THREE_BARS = R.string.accessibility_three_bars
        private val FOUR_BARS = R.string.accessibility_four_bars
        private val FULL_BARS = R.string.accessibility_signal_full
    }
}
+15 −0
Original line number Diff line number Diff line
@@ -1950,6 +1950,21 @@
    <!-- Text displayed indicating that the user might be able to use satellite SOS. -->
    <string name="satellite_emergency_only_carrier_text">Emergency calls or SOS</string>

    <!-- Content description skeleton. Input strings should be carrier name and signal bar description [CHAR LIMIT=NONE]-->
    <string name="accessibility_phone_string_format"><xliff:g id="carrier_name" example="Carrier Name">%1$s</xliff:g>, <xliff:g id="signal_strength_description" example="two bars">%2$s</xliff:g>.</string>
    <!-- Content description describing 0 signal bars. [CHAR LIMIT=NONE] -->
    <string name="accessibility_no_signal">no signal</string>
    <!-- Content description describing 1 signal bar. [CHAR LIMIT=NONE] -->
    <string name="accessibility_one_bar">one bar</string>
    <!-- Content description describing 2 signal bars. [CHAR LIMIT=NONE] -->
    <string name="accessibility_two_bars">two bars</string>
    <!-- Content description describing 3 signal bars. [CHAR LIMIT=NONE] -->
    <string name="accessibility_three_bars">three bars</string>
    <!-- Content description describing 4 signal bars. [CHAR LIMIT=NONE] -->
    <string name="accessibility_four_bars">four bars</string>
    <!-- Content description describing full signal bars. [CHAR LIMIT=NONE] -->
    <string name="accessibility_signal_full">signal full</string>

    <!-- Accessibility label for managed profile icon (not shown on screen) [CHAR LIMIT=NONE] -->
    <string name="accessibility_managed_profile">Work profile</string>

+30 −0
Original line number Diff line number Diff line
/*
 * 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.mobile.ui.binder

import android.view.View
import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription

object MobileContentDescriptionViewBinder {
    fun bind(contentDescription: MobileContentDescription?, view: View) {
        view.contentDescription =
            when (contentDescription) {
                null -> null
                else -> contentDescription.loadContentDescription(view.context)
            }
    }
}
+5 −9
Original line number Diff line number Diff line
@@ -29,9 +29,9 @@ import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.Flags.statusBarStaticInoutIndicators
import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.DarkIconDispatcher
@@ -48,12 +48,8 @@ import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarViewBin
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import com.android.app.tracing.coroutines.launchTraced as launch

private data class Colors(
    @ColorInt val tint: Int,
    @ColorInt val contrast: Int,
)
private data class Colors(@ColorInt val tint: Int, @ColorInt val contrast: Int)

object MobileIconBinder {
    /** Binds the view to the view-model, continuing to update the former based on the latter */
@@ -87,7 +83,7 @@ object MobileIconBinder {
            MutableStateFlow(
                Colors(
                    tint = DarkIconDispatcher.DEFAULT_ICON_TINT,
                    contrast = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT
                    contrast = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT,
                )
            )
        val decorTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
@@ -105,7 +101,7 @@ object MobileIconBinder {
                            viewModel.verboseLogger?.logBinderReceivedVisibility(
                                view,
                                viewModel.subscriptionId,
                                isVisible
                                isVisible,
                            )
                            view.isVisible = isVisible
                            // [StatusIconContainer] can get out of sync sometimes. Make sure to
@@ -152,7 +148,7 @@ object MobileIconBinder {

                    launch {
                        viewModel.contentDescription.distinctUntilChanged().collect {
                            ContentDescriptionViewBinder.bind(it, view)
                            MobileContentDescriptionViewBinder.bind(it, view)
                        }
                    }

+43 −0
Original line number Diff line number Diff line
/*
 * 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.mobile.ui.model

import android.annotation.StringRes
import android.content.Context
import com.android.systemui.res.R

sealed interface MobileContentDescription {
    fun loadContentDescription(context: Context): String

    /**
     * Content description for cellular parameterizes the [networkName] which comes from the system
     */
    data class Cellular(val networkName: String, @StringRes val levelDescriptionRes: Int) :
        MobileContentDescription {
        override fun loadContentDescription(context: Context): String =
            context.getString(
                R.string.accessibility_phone_string_format,
                networkName,
                context.getString(levelDescriptionRes),
            )
    }

    data class SatelliteContentDescription(@StringRes val resId: Int) : MobileContentDescription {
        override fun loadContentDescription(context: Context): String =
            context.getString(this.resId)
    }
}
Loading