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

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

Merge "Use AirplaneModeInteractor to update airplane icon in the status bar" into main

parents b8a4bf7d e3d259ba
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -362,6 +362,13 @@ flag {
    bug: "354930838"
}

flag {
   name: "status_bar_signal_policy_refactor"
   namespace: "systemui"
   description: "Use a settings observer for airplane mode and make StatusBarSignalPolicy startable"
   bug: "264539100"
}

flag {
    name: "status_bar_swipe_over_chip"
    namespace: "systemui"
+38 −6
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.systemui.statusbar.phone;

import static com.android.systemui.Flags.statusBarSignalPolicyRefactor;

import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
@@ -29,10 +31,12 @@ 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.ui.StatusBarIconController;
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor;
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.kotlin.JavaAdapter;

import java.util.ArrayList;
import java.util.List;
@@ -61,15 +65,14 @@ public class StatusBarSignalPolicy implements SignalCallback,
    private final Handler mHandler = Handler.getMain();
    private final CarrierConfigTracker mCarrierConfigTracker;
    private final TunerService mTunerService;
    private final JavaAdapter mJavaAdapter;
    private final AirplaneModeInteractor mAirplaneModeInteractor;

    private boolean mHideAirplane;
    private boolean mHideMobile;
    private boolean mHideEthernet;
    private boolean mActivityEnabled;

    // Track as little state as possible, and only for padding purposes
    private boolean mIsAirplaneMode = false;

    private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>();
    private boolean mInitialized;

@@ -80,15 +83,19 @@ public class StatusBarSignalPolicy implements SignalCallback,
            CarrierConfigTracker carrierConfigTracker,
            NetworkController networkController,
            SecurityController securityController,
            TunerService tunerService
            TunerService tunerService,
            JavaAdapter javaAdapter,
            AirplaneModeInteractor airplaneModeInteractor
    ) {
        mContext = context;

        mIconController = iconController;
        mCarrierConfigTracker = carrierConfigTracker;
        mJavaAdapter = javaAdapter;
        mNetworkController = networkController;
        mSecurityController = securityController;
        mTunerService = tunerService;
        mAirplaneModeInteractor = airplaneModeInteractor;

        mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
        mSlotMobile   = mContext.getString(com.android.internal.R.string.status_bar_mobile);
@@ -109,6 +116,12 @@ public class StatusBarSignalPolicy implements SignalCallback,
        mTunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
        mNetworkController.addCallback(this);
        mSecurityController.addCallback(this);

        if (statusBarSignalPolicyRefactor()) {
            mJavaAdapter.alwaysCollectFlow(
                    mAirplaneModeInteractor.isAirplaneMode(),
                    this::updateAirplaneModeIcon);
        }
    }

    public void destroy() {
@@ -222,15 +235,19 @@ public class StatusBarSignalPolicy implements SignalCallback,

    @Override
    public void setIsAirplaneMode(IconState icon) {
        if (statusBarSignalPolicyRefactor()) {
            return;
        }

        if (DEBUG) {
            Log.d(TAG, "setIsAirplaneMode: "
                    + "icon = " + (icon == null ? "" : icon.toString()));
        }
        mIsAirplaneMode = icon.visible && !mHideAirplane;
        boolean isAirplaneMode = icon.visible && !mHideAirplane;
        int resId = icon.icon;
        String description = icon.contentDescription;

        if (mIsAirplaneMode && resId > 0) {
        if (isAirplaneMode && resId > 0) {
            mIconController.setIcon(mSlotAirplane, resId, description);
            mIconController.setIconVisibility(mSlotAirplane, true);
        } else {
@@ -238,6 +255,21 @@ public class StatusBarSignalPolicy implements SignalCallback,
        }
    }

    public void updateAirplaneModeIcon(boolean isAirplaneModeOn) {
        if (StatusBarSignalPolicyRefactor.isUnexpectedlyInLegacyMode()) {
            return;
        }

        boolean isAirplaneMode = isAirplaneModeOn && !mHideAirplane;
        mIconController.setIconVisibility(mSlotAirplane, isAirplaneMode);
        if (isAirplaneMode) {
            mIconController.setIcon(
                    mSlotAirplane,
                    TelephonyIcons.FLIGHT_MODE_ICON,
                    mContext.getString(R.string.accessibility_airplane_mode));
        }
    }

    /**
     * Stores the statusbar state for no Calling & SMS.
     */
+53 −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.phone

import com.android.systemui.Flags
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils

/** Helper for reading or using the status_bar_signal_policy_refactor flag state. */
@Suppress("NOTHING_TO_INLINE")
object StatusBarSignalPolicyRefactor {
    /** The aconfig flag name */
    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR

    /** A token used for dependency declaration */
    val token: FlagToken
        get() = FlagToken(FLAG_NAME, isEnabled)

    /** Is the refactor enabled */
    @JvmStatic
    inline val isEnabled
        get() = Flags.statusBarSignalPolicyRefactor()

    /**
     * Called to ensure code is only run when the flag is enabled. This protects users from the
     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
     * build to ensure that the refactor author catches issues in testing.
     */
    @JvmStatic
    inline fun isUnexpectedlyInLegacyMode() =
        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)

    /**
     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
     * the flag is enabled to ensure that the refactor author catches issues in testing.
     */
    @JvmStatic
    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
}
+153 −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.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.internal.R
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.Flags.FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.statusbar.connectivity.IconState
import com.android.systemui.statusbar.connectivity.NetworkController
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy_Factory
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor
import com.android.systemui.statusbar.policy.securityController
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.mock
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.junit.runner.RunWith
import org.mockito.Mockito.verify
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.verifyZeroInteractions
import kotlin.test.Test

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class StatusBarSignalPolicyTest : SysuiTestCase() {
    private val kosmos = Kosmos().also { it.testCase = this }

    private lateinit var underTest: StatusBarSignalPolicy

    private val testScope = TestScope()

    private val javaAdapter = JavaAdapter(testScope.backgroundScope)
    private val airplaneModeInteractor = kosmos.airplaneModeInteractor
    private val securityController = kosmos.securityController

    private val tunerService = mock<TunerService>()
    private val statusBarIconController = mock<StatusBarIconController>()
    private val networkController = mock<NetworkController>()
    private val carrierConfigTracker = mock<CarrierConfigTracker>()

    private var slotAirplane: String? = null

    @Before
    fun setup() {
        underTest =
            StatusBarSignalPolicy_Factory.newInstance(
                mContext,
                statusBarIconController,
                carrierConfigTracker,
                networkController,
                securityController,
                tunerService,
                javaAdapter,
                airplaneModeInteractor,
            )

        slotAirplane = mContext.getString(R.string.status_bar_airplane)
    }

    @Test
    @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
    fun airplaneModeViaInteractor_statusBarSignalPolicyRefactorFlagEnabled_iconUpdated() =
        testScope.runTest {
            underTest.init()
            airplaneModeInteractor.setIsAirplaneMode(true)
            runCurrent()
            verify(statusBarIconController).setIconVisibility(slotAirplane, true)

            airplaneModeInteractor.setIsAirplaneMode(false)
            runCurrent()
            verify(statusBarIconController).setIconVisibility(slotAirplane, false)
        }

    @Test
    @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
    fun airplaneModeViaSignalCallback_statusBarSignalPolicyRefactorFlagEnabled_iconNotUpdated() =
        testScope.runTest {
            underTest.init()
            runCurrent()
            clearInvocations(statusBarIconController)

            // Make sure the legacy code path does not change airplane mode when the refactor
            // flag is enabled.
            underTest.setIsAirplaneMode(IconState(true, TelephonyIcons.FLIGHT_MODE_ICON, ""))
            runCurrent()
            verifyZeroInteractions(statusBarIconController)

            underTest.setIsAirplaneMode(IconState(false, TelephonyIcons.FLIGHT_MODE_ICON, ""))
            runCurrent()
            verifyZeroInteractions(statusBarIconController)
        }

    @Test
    @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
    fun airplaneModeViaSignalCallback_statusBarSignalPolicyRefactorFlagDisabled_iconUpdated() =
        testScope.runTest {
            underTest.init()

            underTest.setIsAirplaneMode(IconState(true, TelephonyIcons.FLIGHT_MODE_ICON, ""))
            runCurrent()
            verify(statusBarIconController).setIconVisibility(slotAirplane, true)

            underTest.setIsAirplaneMode(IconState(false, TelephonyIcons.FLIGHT_MODE_ICON, ""))
            runCurrent()
            verify(statusBarIconController).setIconVisibility(slotAirplane, false)
        }

    @Test
    @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
    fun airplaneModeViaInteractor_statusBarSignalPolicyRefactorFlagDisabled_iconNotUpdated() =
        testScope.runTest {
            underTest.init()

            // Make sure changing airplane mode from airplaneModeRepository does nothing
            // if the StatusBarSignalPolicyRefactor is not enabled.
            airplaneModeInteractor.setIsAirplaneMode(true)
            runCurrent()
            verifyZeroInteractions(statusBarIconController)

            airplaneModeInteractor.setIsAirplaneMode(false)
            runCurrent()
            verifyZeroInteractions(statusBarIconController)
        }
}