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

Commit 3cdb3212 authored by Evan Laird's avatar Evan Laird
Browse files

[Networking] Remove OperatorNameViewController as a SignalCallback

This class was only listening to `SignalCallback` to check for airplane
mode changes.

This CL swaps out the signal callback for a direct listener on
AirplaneModeInteractor. It also changes the view controller to be more
testable by injecting the SubscriptionManagerProxy. And now we have
tests!

Test: OperatorNameViewControllerTest
Bug: 291321279
Flag: NONE
Change-Id: I426539c2e04b87190ed256e64e7d881d86ee2bd7
parent d05b16c3
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -19,8 +19,6 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;

import com.android.settingslib.WirelessUtils;

/** Shows the operator name */
public class OperatorNameView extends TextView {
    private boolean mDemoMode;
@@ -41,13 +39,14 @@ public class OperatorNameView extends TextView {
        mDemoMode = demoMode;
    }

    void update(boolean showOperatorName,
    void update(
            boolean showOperatorName,
            boolean hasMobile,
            boolean airplaneMode,
            OperatorNameViewController.SubInfo sub
    ) {
        setVisibility(showOperatorName ? VISIBLE : GONE);

        boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext);
        if (!hasMobile || airplaneMode) {
            setText(null);
            setVisibility(GONE);
+44 −26
Original line number Diff line number Diff line
@@ -16,11 +16,9 @@

package com.android.systemui.statusbar;

import android.annotation.NonNull;
import android.os.Bundle;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.view.View;

@@ -28,47 +26,60 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor;
import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.kotlin.JavaAdapter;

import javax.inject.Inject;

import kotlinx.coroutines.Job;

/** Controller for {@link OperatorNameView}. */
public class OperatorNameViewController extends ViewController<OperatorNameView> {
    private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name";

    private final DarkIconDispatcher mDarkIconDispatcher;
    private final NetworkController mNetworkController;
    private final TunerService mTunerService;
    private final TelephonyManager mTelephonyManager;
    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    private final CarrierConfigTracker mCarrierConfigTracker;
    private final AirplaneModeInteractor mAirplaneModeInteractor;
    private final SubscriptionManagerProxy mSubscriptionManagerProxy;
    private final JavaAdapter mJavaAdapter;

    private Job mAirplaneModeJob;

    private OperatorNameViewController(OperatorNameView view,
            DarkIconDispatcher darkIconDispatcher,
            NetworkController networkController,
            TunerService tunerService,
            TelephonyManager telephonyManager,
            KeyguardUpdateMonitor keyguardUpdateMonitor,
            CarrierConfigTracker carrierConfigTracker) {
            CarrierConfigTracker carrierConfigTracker,
            AirplaneModeInteractor airplaneModeInteractor,
            SubscriptionManagerProxy subscriptionManagerProxy,
            JavaAdapter javaAdapter) {
        super(view);
        mDarkIconDispatcher = darkIconDispatcher;
        mNetworkController = networkController;
        mTunerService = tunerService;
        mTelephonyManager = telephonyManager;
        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
        mCarrierConfigTracker = carrierConfigTracker;
        mAirplaneModeInteractor = airplaneModeInteractor;
        mSubscriptionManagerProxy = subscriptionManagerProxy;
        mJavaAdapter = javaAdapter;
    }

    @Override
    protected void onViewAttached() {
        mDarkIconDispatcher.addDarkReceiver(mDarkReceiver);
        mNetworkController.addCallback(mSignalCallback);
        mAirplaneModeJob =
                mJavaAdapter.alwaysCollectFlow(
                        mAirplaneModeInteractor.isAirplaneMode(),
                        (isAirplaneMode) -> update());
        mTunerService.addTunable(mTunable, KEY_SHOW_OPERATOR_NAME);
        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
    }
@@ -76,7 +87,7 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
    @Override
    protected void onViewDetached() {
        mDarkIconDispatcher.removeDarkReceiver(mDarkReceiver);
        mNetworkController.removeCallback(mSignalCallback);
        mAirplaneModeJob.cancel(null);
        mTunerService.removeTunable(mTunable);
        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
    }
@@ -87,11 +98,17 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
                mCarrierConfigTracker
                        .getShowOperatorNameInStatusBarConfig(defaultSubInfo.getSubId())
                        && (mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0);
        mView.update(showOperatorName, mTelephonyManager.isDataCapable(), getDefaultSubInfo());
        mView.update(
                showOperatorName,
                mTelephonyManager.isDataCapable(),
                mAirplaneModeInteractor.isAirplaneMode().getValue(),
                getDefaultSubInfo()
        );
    }

    private SubInfo getDefaultSubInfo() {
        int defaultSubId = SubscriptionManager.getDefaultDataSubscriptionId();
        int defaultSubId = mSubscriptionManagerProxy.getDefaultDataSubscriptionId();

        SubscriptionInfo sI = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(defaultSubId);
        return new SubInfo(
                sI.getSubscriptionId(),
@@ -103,36 +120,44 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
    /** Factory for constructing an {@link OperatorNameViewController}. */
    public static class Factory {
        private final DarkIconDispatcher mDarkIconDispatcher;
        private final NetworkController mNetworkController;
        private final TunerService mTunerService;
        private final TelephonyManager mTelephonyManager;
        private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
        private final CarrierConfigTracker mCarrierConfigTracker;
        private final AirplaneModeInteractor mAirplaneModeInteractor;
        private final SubscriptionManagerProxy mSubscriptionManagerProxy;
        private final JavaAdapter mJavaAdapter;

        @Inject
        public Factory(DarkIconDispatcher darkIconDispatcher,
                NetworkController networkController,
                TunerService tunerService,
                TelephonyManager telephonyManager,
                KeyguardUpdateMonitor keyguardUpdateMonitor,
                CarrierConfigTracker carrierConfigTracker) {
                CarrierConfigTracker carrierConfigTracker,
                AirplaneModeInteractor airplaneModeInteractor,
                SubscriptionManagerProxy subscriptionManagerProxy,
                JavaAdapter javaAdapter) {
            mDarkIconDispatcher = darkIconDispatcher;
            mNetworkController = networkController;
            mTunerService = tunerService;
            mTelephonyManager = telephonyManager;
            mKeyguardUpdateMonitor = keyguardUpdateMonitor;
            mCarrierConfigTracker = carrierConfigTracker;
            mAirplaneModeInteractor = airplaneModeInteractor;
            mSubscriptionManagerProxy = subscriptionManagerProxy;
            mJavaAdapter = javaAdapter;
        }

        /** Create an {@link OperatorNameViewController}. */
        public OperatorNameViewController create(OperatorNameView view) {
            return new OperatorNameViewController(view,
                    mDarkIconDispatcher,
                    mNetworkController,
                    mTunerService,
                    mTelephonyManager,
                    mKeyguardUpdateMonitor,
                    mCarrierConfigTracker);
                    mCarrierConfigTracker,
                    mAirplaneModeInteractor,
                    mSubscriptionManagerProxy,
                    mJavaAdapter);
        }
    }

@@ -149,13 +174,6 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
            (area, darkIntensity, tint) ->
                    mView.setTextColor(DarkIconDispatcher.getTint(area, mView, tint));

    private final SignalCallback mSignalCallback = new SignalCallback() {
        @Override
        public void setIsAirplaneMode(@NonNull IconState icon) {
            update();
        }
    };

    private final TunerService.Tunable mTunable = (key, newValue) -> update();


+2 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlo
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map

/**
@@ -40,7 +41,7 @@ constructor(
    private val mobileConnectionsRepository: MobileConnectionsRepository,
) {
    /** True if the device is currently in airplane mode. */
    val isAirplaneMode: Flow<Boolean> = airplaneModeRepository.isAirplaneMode
    val isAirplaneMode: StateFlow<Boolean> = airplaneModeRepository.isAirplaneMode

    /** True if we're configured to force-hide the airplane mode icon and false otherwise. */
    val isForceHidden: Flow<Boolean> =
+202 −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

import android.telephony.ServiceState
import android.telephony.SubscriptionInfo
import android.telephony.TelephonyManager
import android.telephony.telephonyManager
import androidx.test.filters.SmallTest
import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.plugins.DarkIconDispatcher
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.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertTrue
import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.mockito.Mock
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class OperatorNameViewControllerTest : SysuiTestCase() {
    private lateinit var underTest: OperatorNameViewController
    private lateinit var airplaneModeInteractor: AirplaneModeInteractor

    private val kosmos = Kosmos()
    private val testScope = TestScope()

    private val view = OperatorNameView(mContext)
    private val javaAdapter = JavaAdapter(testScope.backgroundScope)

    @Mock private lateinit var darkIconDispatcher: DarkIconDispatcher
    @Mock private lateinit var tunerService: TunerService
    private var telephonyManager = kosmos.telephonyManager
    private val keyguardUpdateMonitor = kosmos.keyguardUpdateMonitor
    @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
    private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()

    private val airplaneModeRepository = FakeAirplaneModeRepository()
    private val connectivityRepository = FakeConnectivityRepository()
    private val mobileConnectionsRepository = FakeMobileConnectionsRepository()

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)

        airplaneModeInteractor =
            AirplaneModeInteractor(
                airplaneModeRepository,
                connectivityRepository,
                mobileConnectionsRepository,
            )

        underTest =
            OperatorNameViewController.Factory(
                    darkIconDispatcher,
                    tunerService,
                    telephonyManager,
                    keyguardUpdateMonitor,
                    carrierConfigTracker,
                    airplaneModeInteractor,
                    subscriptionManagerProxy,
                    javaAdapter,
                )
                .create(view)
    }

    @Test
    fun updateFromSubInfo_showsCarrieName() =
        testScope.runTest {
            whenever(telephonyManager.isDataCapable).thenReturn(true)

            val mockSubInfo =
                mock<SubscriptionInfo>().also {
                    whenever(it.subscriptionId).thenReturn(1)
                    whenever(it.carrierName).thenReturn("test_carrier")
                }
            whenever(keyguardUpdateMonitor.getSubscriptionInfoForSubId(any()))
                .thenReturn(mockSubInfo)
            whenever(keyguardUpdateMonitor.getSimState(any()))
                .thenReturn(TelephonyManager.SIM_STATE_READY)
            whenever(keyguardUpdateMonitor.getServiceState(any()))
                .thenReturn(ServiceState().also { it.state = ServiceState.STATE_IN_SERVICE })
            subscriptionManagerProxy.defaultDataSubId = 1
            airplaneModeRepository.setIsAirplaneMode(false)

            underTest.onViewAttached()
            runCurrent()

            assertThat(view.text).isEqualTo("test_carrier")
        }

    @Test
    fun notDataCapable_doesNotShowOperatorName() =
        testScope.runTest {
            whenever(telephonyManager.isDataCapable).thenReturn(false)

            val mockSubInfo =
                mock<SubscriptionInfo>().also {
                    whenever(it.subscriptionId).thenReturn(1)
                    whenever(it.carrierName).thenReturn("test_carrier")
                }
            whenever(keyguardUpdateMonitor.getSubscriptionInfoForSubId(any()))
                .thenReturn(mockSubInfo)
            whenever(keyguardUpdateMonitor.getSimState(any()))
                .thenReturn(TelephonyManager.SIM_STATE_READY)
            whenever(keyguardUpdateMonitor.getServiceState(any()))
                .thenReturn(ServiceState().also { it.state = ServiceState.STATE_IN_SERVICE })
            subscriptionManagerProxy.defaultDataSubId = 1
            airplaneModeRepository.setIsAirplaneMode(false)

            underTest.onViewAttached()
            runCurrent()

            assertTrue(view.text.isNullOrEmpty())
        }

    @Test
    fun airplaneMode_doesNotShowOperatorName() =
        testScope.runTest {
            whenever(telephonyManager.isDataCapable).thenReturn(false)
            val mockSubInfo =
                mock<SubscriptionInfo>().also {
                    whenever(it.subscriptionId).thenReturn(1)
                    whenever(it.carrierName).thenReturn("test_carrier")
                }
            whenever(keyguardUpdateMonitor.getSubscriptionInfoForSubId(any()))
                .thenReturn(mockSubInfo)
            whenever(keyguardUpdateMonitor.getSimState(any()))
                .thenReturn(TelephonyManager.SIM_STATE_READY)
            whenever(keyguardUpdateMonitor.getServiceState(any()))
                .thenReturn(ServiceState().also { it.state = ServiceState.STATE_IN_SERVICE })
            subscriptionManagerProxy.defaultDataSubId = 1
            airplaneModeRepository.setIsAirplaneMode(true)

            underTest.onViewAttached()
            runCurrent()

            assertTrue(view.text.isNullOrEmpty())
        }

    @Test
    fun notInService_doesNotShowOperatorName() =
        testScope.runTest {
            // Data capable
            whenever(telephonyManager.isDataCapable).thenReturn(true)

            // Valid subscription
            val mockSubInfo =
                mock<SubscriptionInfo>().also {
                    whenever(it.subscriptionId).thenReturn(1)
                    whenever(it.carrierName).thenReturn("test_carrier")
                }
            whenever(keyguardUpdateMonitor.getSubscriptionInfoForSubId(any()))
                .thenReturn(mockSubInfo)
            whenever(keyguardUpdateMonitor.getSimState(any()))
                .thenReturn(TelephonyManager.SIM_STATE_READY)

            // Not in service
            whenever(keyguardUpdateMonitor.getServiceState(any()))
                .thenReturn(ServiceState().also { it.state = ServiceState.STATE_OUT_OF_SERVICE })
            // Subscription is default for data
            subscriptionManagerProxy.defaultDataSubId = 1
            // Not airplane mode
            airplaneModeRepository.setIsAirplaneMode(false)

            underTest.onViewAttached()
            runCurrent()

            assertTrue(view.text.isNullOrEmpty())
        }
}