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

Commit 896647b8 authored by Evan Laird's avatar Evan Laird Committed by Automerger Merge Worker
Browse files

Merge changes from topics "icon-interactor", "wifi-networks" into udc-qpr-dev...

Merge changes from topics "icon-interactor", "wifi-networks" into udc-qpr-dev am: 0ee361bf am: 39e8cec9

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/24436350



Change-Id: I9ebd8170e7c507ee04904362eaf01db069e8c09b
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents e9b37196 39e8cec9
Loading
Loading
Loading
Loading
+114 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.qs.tiles

import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.view.View
import com.android.internal.logging.MetricsLogger
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.AlphaControlledSignalTileView
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
import com.android.systemui.statusbar.connectivity.AccessPointController
import com.android.systemui.statusbar.pipeline.shared.ui.binder.InternetTileBinder
import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileModel
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.InternetTileViewModel
import javax.inject.Inject

class InternetTileNewImpl
@Inject
constructor(
    host: QSHost,
    uiEventLogger: QsEventLogger,
    @Background backgroundLooper: Looper,
    @Main private val mainHandler: Handler,
    falsingManager: FalsingManager,
    metricsLogger: MetricsLogger,
    statusBarStateController: StatusBarStateController,
    activityStarter: ActivityStarter,
    qsLogger: QSLogger,
    viewModel: InternetTileViewModel,
    private val internetDialogFactory: InternetDialogFactory,
    private val accessPointController: AccessPointController,
) :
    QSTileImpl<QSTile.SignalState>(
        host,
        uiEventLogger,
        backgroundLooper,
        mainHandler,
        falsingManager,
        metricsLogger,
        statusBarStateController,
        activityStarter,
        qsLogger
    ) {
    private var model: InternetTileModel = viewModel.tileModel.value

    init {
        InternetTileBinder.bind(lifecycle, viewModel.tileModel) { newModel ->
            model = newModel
            refreshState()
        }
    }

    override fun createTileView(context: Context): QSIconView =
        AlphaControlledSignalTileView(context)

    override fun getTileLabel(): CharSequence =
        mContext.getString(R.string.quick_settings_internet_label)

    override fun newTileState(): QSTile.SignalState {
        return QSTile.SignalState().also { it.forceExpandIcon = true }
    }

    override fun handleClick(view: View?) {
        mainHandler.post {
            internetDialogFactory.create(
                aboveStatusBar = true,
                accessPointController.canConfigMobileData(),
                accessPointController.canConfigWifi(),
                view,
            )
        }
    }

    override fun handleUpdateState(state: QSTile.SignalState, arg: Any?) {
        state.label = mContext.resources.getString(R.string.quick_settings_internet_label)

        model.applyTo(state, mContext)
    }

    override fun getLongClickIntent(): Intent = WIFI_SETTINGS

    companion object {
        private val WIFI_SETTINGS = Intent(Settings.ACTION_WIFI_SETTINGS)
    }
}
+21 −6
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.systemui.statusbar.connectivity

import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.AirplaneModeTile
import com.android.systemui.qs.tiles.BluetoothTile
@@ -23,21 +25,17 @@ import com.android.systemui.qs.tiles.CastTile
import com.android.systemui.qs.tiles.DataSaverTile
import com.android.systemui.qs.tiles.HotspotTile
import com.android.systemui.qs.tiles.InternetTile
import com.android.systemui.qs.tiles.InternetTileNewImpl
import com.android.systemui.qs.tiles.NfcTile
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey

@Module
interface ConnectivityModule {

    /** Inject InternetTile into tileMap in QSModule */
    @Binds
    @IntoMap
    @StringKey(InternetTile.TILE_SPEC)
    fun bindInternetTile(internetTile: InternetTile): QSTileImpl<*>

    /** Inject BluetoothTile into tileMap in QSModule */
    @Binds
    @IntoMap
@@ -70,4 +68,21 @@ interface ConnectivityModule {

    /** Inject NfcTile into tileMap in QSModule */
    @Binds @IntoMap @StringKey(NfcTile.TILE_SPEC) fun bindNfcTile(nfcTile: NfcTile): QSTileImpl<*>

    companion object {
        /** Inject InternetTile or InternetTileNewImpl into tileMap in QSModule */
        @Provides
        @IntoMap
        @StringKey(InternetTile.TILE_SPEC)
        fun bindInternetTile(
            internetTile: InternetTile,
            newInternetTile: InternetTileNewImpl,
            featureFlags: FeatureFlags,
        ): QSTileImpl<*> =
            if (featureFlags.isEnabled(SIGNAL_CALLBACK_DEPRECATION)) {
                newInternetTile
            } else {
                internetTile
            }
    }
}
+241 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.qs.tiles

import android.os.Handler
import android.service.quicksettings.Tile
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
import com.android.systemui.statusbar.connectivity.AccessPointController
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
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.model.DefaultConnectionModel.Wifi
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.InternetTileViewModel
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
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWithLooper(setAsMainLooper = true)
class InternetTileNewImplTest : SysuiTestCase() {
    lateinit var underTest: InternetTileNewImpl

    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)

    private var airplaneModeRepository = FakeAirplaneModeRepository()
    private var connectivityRepository = FakeConnectivityRepository()
    private var ethernetInteractor = EthernetInteractor(connectivityRepository)
    private var mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
    private var wifiRepository = FakeWifiRepository()
    private var wifiInteractor =
        WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
    private lateinit var viewModel: InternetTileViewModel

    private lateinit var looper: TestableLooper

    @Mock private lateinit var host: QSHost
    @Mock private lateinit var eventLogger: QsEventLogger
    @Mock private lateinit var metricsLogger: MetricsLogger
    @Mock private lateinit var sbStateController: StatusBarStateController
    @Mock private lateinit var activityStarter: ActivityStarter
    @Mock private lateinit var logger: QSLogger
    @Mock private lateinit var dialogFactory: InternetDialogFactory
    @Mock private lateinit var accessPointController: AccessPointController

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        looper = TestableLooper.get(this)

        // Allow the tile to load resources
        whenever(host.context).thenReturn(context)
        whenever(host.userContext).thenReturn(context)

        viewModel =
            InternetTileViewModel(
                airplaneModeRepository,
                connectivityRepository,
                ethernetInteractor,
                mobileIconsInteractor,
                wifiInteractor,
                context,
                testScope.backgroundScope,
            )

        underTest =
            InternetTileNewImpl(
                host,
                eventLogger,
                looper.looper,
                Handler(looper.looper),
                FalsingManagerFake(),
                metricsLogger,
                sbStateController,
                activityStarter,
                logger,
                viewModel,
                dialogFactory,
                accessPointController
            )

        underTest.initialize()
        underTest.setListening(Object(), true)

        looper.processAllMessages()
    }

    @Test
    fun noDefaultConnection_noNetworkAvailable() =
        testScope.runTest {
            connectivityRepository.defaultConnections.value = DefaultConnectionModel()
            wifiRepository.wifiScanResults.value = listOf()

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.secondaryLabel.toString())
                .isEqualTo(context.getString(R.string.quick_settings_networks_unavailable))
            assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
        }

    @Test
    fun noDefaultConnection_networksAvailable() =
        testScope.runTest {
            connectivityRepository.defaultConnections.value = DefaultConnectionModel()
            wifiRepository.wifiScanResults.value =
                listOf(
                    WifiScanEntry(ssid = "ssid 1"),
                    WifiScanEntry(ssid = "ssid 2"),
                )

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.secondaryLabel.toString())
                .isEqualTo(context.getString(R.string.quick_settings_networks_available))
            assertThat(underTest.state.state).isEqualTo(1)
        }

    @Test
    fun airplaneMode_enabled_wifiDisabled() =
        testScope.runTest {
            airplaneModeRepository.setIsAirplaneMode(true)
            connectivityRepository.defaultConnections.value = DefaultConnectionModel()
            wifiRepository.setIsWifiEnabled(false)

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
            assertThat(underTest.state.secondaryLabel)
                .isEqualTo(context.getString(R.string.status_bar_airplane))
        }

    @Test
    fun airplaneMode_enabled_wifiEnabledButNotConnected() =
        testScope.runTest {
            airplaneModeRepository.setIsAirplaneMode(true)
            connectivityRepository.defaultConnections.value = DefaultConnectionModel()
            wifiRepository.setIsWifiEnabled(true)

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
            assertThat(underTest.state.secondaryLabel)
                .isEqualTo(context.getString(R.string.status_bar_airplane))
        }

    @Test
    fun airplaneMode_enabled_wifiEnabledAndConnected() =
        testScope.runTest {
            airplaneModeRepository.setIsAirplaneMode(true)
            connectivityRepository.defaultConnections.value =
                DefaultConnectionModel(
                    wifi = Wifi(true),
                    isValidated = true,
                )
            wifiRepository.setIsWifiEnabled(true)
            wifiRepository.setWifiNetwork(ACTIVE_WIFI)

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.state).isEqualTo(Tile.STATE_ACTIVE)
            assertThat(underTest.state.secondaryLabel).isEqualTo(WIFI_SSID)
        }

    @Test
    fun wifiConnected() =
        testScope.runTest {
            connectivityRepository.defaultConnections.value =
                DefaultConnectionModel(
                    wifi = Wifi(true),
                    isValidated = true,
                )

            wifiRepository.setIsWifiEnabled(true)
            wifiRepository.setWifiNetwork(ACTIVE_WIFI)

            runCurrent()
            looper.processAllMessages()

            assertThat(underTest.state.state).isEqualTo(Tile.STATE_ACTIVE)
            assertThat(underTest.state.secondaryLabel).isEqualTo(WIFI_SSID)
        }

    companion object {
        const val WIFI_SSID = "test ssid"
        val ACTIVE_WIFI =
            WifiNetworkModel.Active(
                networkId = 1,
                isValidated = true,
                level = 4,
                ssid = WIFI_SSID,
            )
    }
}