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

Commit 14fcc462 authored by Matías Hernández's avatar Matías Hernández
Browse files

Prepare the way for showing the active Priority Mode icon instead of the DND icon

* Collect ZenModeInteractor.mainActiveMode in PhoneStatusBarPolicy.
* Show "a mode icon" instead of the DND icon when a mode is active.

Bug: 360399800
Test: atest PhoneStatusBarPolicyTest
Flag: android.app.modes_ui
Change-Id: Ied8e53c2961803a4e53191c582ebacd9d61193d5
parent 1debc7ed
Loading
Loading
Loading
Loading
+74 −23
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import android.view.View;

import androidx.lifecycle.Observer;

import com.android.settingslib.notification.modes.ZenMode;
import com.android.systemui.Flags;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -78,6 +79,7 @@ import com.android.systemui.statusbar.policy.RotationLockController.RotationLock
import com.android.systemui.statusbar.policy.SensorPrivacyController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.DateFormatUtil;
@@ -99,7 +101,6 @@ public class PhoneStatusBarPolicy
                CommandQueue.Callbacks,
                RotationLockControllerCallback,
                Listener,
                ZenModeController.Callback,
                DeviceProvisionedListener,
                KeyguardStateController.Callback,
                PrivacyItemController.Callback,
@@ -161,6 +162,7 @@ public class PhoneStatusBarPolicy
    private final RecordingController mRecordingController;
    private final RingerModeTracker mRingerModeTracker;
    private final PrivacyLogger mPrivacyLogger;
    private final ZenModeInteractor mZenModeInteractor;

    private boolean mZenVisible;
    private boolean mVibrateVisible;
@@ -193,6 +195,7 @@ public class PhoneStatusBarPolicy
            PrivacyItemController privacyItemController,
            PrivacyLogger privacyLogger,
            ConnectedDisplayInteractor connectedDisplayInteractor,
            ZenModeInteractor zenModeInteractor,
            JavaAdapter javaAdapter
    ) {
        mIconController = iconController;
@@ -224,6 +227,7 @@ public class PhoneStatusBarPolicy
        mTelecomManager = telecomManager;
        mRingerModeTracker = ringerModeTracker;
        mPrivacyLogger = privacyLogger;
        mZenModeInteractor = zenModeInteractor;
        mJavaAdapter = javaAdapter;

        mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast);
@@ -355,7 +359,13 @@ public class PhoneStatusBarPolicy
        mBluetooth.addCallback(this);
        mProvisionedController.addCallback(this);
        mCurrentUserSetup = mProvisionedController.isCurrentUserSetup();
        mZenController.addCallback(this);
        if (usesModeIcons()) {
            // Note that we're not fully replacing ZenModeController with ZenModeInteractor, so
            // we listen for the extra event here but still add the ZMC callback.
            mJavaAdapter.alwaysCollectFlow(mZenModeInteractor.getMainActiveMode(),
                    this::onActiveModeChanged);
        }
        mZenController.addCallback(mZenControllerCallback);
        if (!Flags.statusBarScreenSharingChips()) {
            // If the flag is enabled, the cast icon is handled in the new screen sharing chips
            // instead of here so we don't need to listen for events here.
@@ -385,6 +395,26 @@ public class PhoneStatusBarPolicy
                () -> mResources.getString(R.string.accessibility_managed_profile));
    }

    private void onActiveModeChanged(@Nullable ZenMode zenMode) {
        if (!usesModeIcons()) {
            Log.wtf(TAG, "onActiveModeChanged shouldn't be called if MODES_UI_ICONS is disabled");
            return;
        }
        boolean visible = zenMode != null;
        if (visible) {
            // TODO: b/360399800 - Get the drawable from the mode; this is a placeholder.
            mIconController.setIcon(mSlotZen,
                    com.android.internal.R.drawable.ic_zen_mode_type_immersive, zenMode.getName());
        }
        if (visible != mZenVisible) {
            mIconController.setIconVisibility(mSlotZen, visible);
            mZenVisible = visible;
        }
    }

    // TODO: b/308591859 - Should be removed and use the ZenModeInteractor only.
    private final ZenModeController.Callback mZenControllerCallback =
            new ZenModeController.Callback() {
                @Override
                public void onZenChanged(int zen) {
                    updateVolumeZen();
@@ -394,6 +424,7 @@ public class PhoneStatusBarPolicy
                public void onConsolidatedPolicyChanged(NotificationManager.Policy policy) {
                    updateVolumeZen();
                }
            };

    private void updateAlarm() {
        final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(mUserTracker.getUserId());
@@ -417,15 +448,24 @@ public class PhoneStatusBarPolicy
        return mResources.getString(R.string.accessibility_quick_settings_alarm, dateString);
    }

    private final void updateVolumeZen() {
    private void updateVolumeZen() {
        int zen = mZenController.getZen();
        if (!usesModeIcons()) {
            updateZenIcon(zen);
        }
        updateRingerAndAlarmIcons(zen);
    }

    private void updateZenIcon(int zen) {
        if (usesModeIcons()) {
            Log.wtf(TAG, "updateZenIcon shouldn't be called if MODES_UI_ICONS is enabled");
            return;
        }

        boolean zenVisible = false;
        int zenIconId = 0;
        String zenDescription = null;

        boolean vibrateVisible = false;
        boolean muteVisible = false;
        int zen = mZenController.getZen();

        if (DndTile.isVisible(mSharedPreferences) || DndTile.isCombinedIcon(mSharedPreferences)) {
            zenVisible = zen != Global.ZEN_MODE_OFF;
            zenIconId = R.drawable.stat_sys_dnd;
@@ -440,7 +480,21 @@ public class PhoneStatusBarPolicy
            zenDescription = mResources.getString(R.string.interruption_level_priority);
        }

        if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConsolidatedPolicy())) {
        if (zenVisible) {
            mIconController.setIcon(mSlotZen, zenIconId, zenDescription);
        }
        if (zenVisible != mZenVisible) {
            mIconController.setIconVisibility(mSlotZen, zenVisible);
            mZenVisible = zenVisible;
        }
    }

    private void updateRingerAndAlarmIcons(int zen) {
        boolean vibrateVisible = false;
        boolean muteVisible = false;

        NotificationManager.Policy consolidatedPolicy = mZenController.getConsolidatedPolicy();
        if (!ZenModeConfig.isZenOverridingRinger(zen, consolidatedPolicy)) {
            final Integer ringerModeInternal =
                    mRingerModeTracker.getRingerModeInternal().getValue();
            if (ringerModeInternal != null) {
@@ -452,14 +506,6 @@ public class PhoneStatusBarPolicy
            }
        }

        if (zenVisible) {
            mIconController.setIcon(mSlotZen, zenIconId, zenDescription);
        }
        if (zenVisible != mZenVisible) {
            mIconController.setIconVisibility(mSlotZen, zenVisible);
            mZenVisible = zenVisible;
        }

        if (vibrateVisible != mVibrateVisible) {
            mIconController.setIconVisibility(mSlotVibrate, vibrateVisible);
            mVibrateVisible = vibrateVisible;
@@ -888,4 +934,9 @@ public class PhoneStatusBarPolicy

        mIconController.setIconVisibility(mSlotConnectedDisplay, visible);
    }

    private static boolean usesModeIcons() {
        return android.app.Flags.modesApi() && android.app.Flags.modesUi()
                && android.app.Flags.modesUiIcons();
    }
}
+124 −3
Original line number Diff line number Diff line
@@ -17,12 +17,16 @@
package com.android.systemui.statusbar.phone

import android.app.AlarmManager
import android.app.NotificationManager
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyResourcesManager
import android.content.SharedPreferences
import android.net.Uri
import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.service.notification.ZenModeConfig
import android.telecom.TelecomManager
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
@@ -53,12 +57,13 @@ import com.android.systemui.statusbar.policy.RotationLockController
import com.android.systemui.statusbar.policy.SensorPrivacyController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.RingerModeTracker
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.DateFormatUtil
import com.android.systemui.util.time.FakeSystemClock
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -83,7 +88,10 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.reset

@RunWith(AndroidJUnit4::class)
@RunWithLooper
@@ -91,7 +99,11 @@ import org.mockito.kotlin.argumentCaptor
@SmallTest
class PhoneStatusBarPolicyTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val zenModeRepository = kosmos.fakeZenModeRepository

    companion object {
        private const val ZEN_SLOT = "zen"
        private const val ALARM_SLOT = "alarm"
        private const val CAST_SLOT = "cast"
        private const val SCREEN_RECORD_SLOT = "screen_record"
@@ -109,7 +121,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
    @Mock private lateinit var userInfoController: UserInfoController
    @Mock private lateinit var rotationLockController: RotationLockController
    @Mock private lateinit var dataSaverController: DataSaverController
    @Mock private lateinit var zenModeController: ZenModeController
    @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
    @Mock private lateinit var keyguardStateController: KeyguardStateController
    @Mock private lateinit var locationController: LocationController
@@ -133,6 +144,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {

    private val testScope = TestScope(UnconfinedTestDispatcher())
    private val fakeConnectedDisplayStateProvider = FakeConnectedDisplayStateProvider()
    private val zenModeController = FakeZenModeController()

    private lateinit var executor: FakeExecutor
    private lateinit var statusBarPolicy: PhoneStatusBarPolicy
@@ -374,6 +386,67 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
        verify(iconController, never()).setIconVisibility(eq(SCREEN_RECORD_SLOT), any())
    }

    @Test
    @EnableFlags(android.app.Flags.FLAG_MODES_UI_ICONS)
    fun zenModeInteractorActiveModeChanged_showsModeIcon() =
        testScope.runTest {
            statusBarPolicy.init()
            reset(iconController)

            zenModeRepository.addMode(id = "Bedtime", active = true)
            runCurrent()

            verify(iconController).setIconVisibility(eq(ZEN_SLOT), eq(true))
            verify(iconController).setIcon(eq(ZEN_SLOT), anyInt(), eq("Mode Bedtime"))

            zenModeRepository.deactivateMode("Bedtime")
            runCurrent()

            verify(iconController).setIconVisibility(eq(ZEN_SLOT), eq(false))
        }

    @Test
    @EnableFlags(android.app.Flags.FLAG_MODES_UI_ICONS)
    fun zenModeControllerOnGlobalZenChanged_doesNotUpdateDndIcon() {
        statusBarPolicy.init()
        reset(iconController)

        zenModeController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null)

        verify(iconController, never()).setIconVisibility(eq(ZEN_SLOT), any())
        verify(iconController, never()).setIcon(eq(ZEN_SLOT), anyInt(), any())
    }

    @Test
    @DisableFlags(android.app.Flags.FLAG_MODES_UI_ICONS)
    fun zenModeInteractorActiveModeChanged_withFlagDisabled_ignored() =
        testScope.runTest {
            statusBarPolicy.init()
            reset(iconController)

            zenModeRepository.addMode(id = "Bedtime", active = true)
            runCurrent()

            verify(iconController, never()).setIconVisibility(eq(ZEN_SLOT), any())
            verify(iconController, never()).setIcon(eq(ZEN_SLOT), anyInt(), any())
        }

    @Test
    @DisableFlags(android.app.Flags.FLAG_MODES_UI_ICONS)
    fun zenModeControllerOnGlobalZenChanged_withFlagDisabled_updatesDndIcon() {
        statusBarPolicy.init()
        reset(iconController)

        zenModeController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null)

        verify(iconController).setIconVisibility(eq(ZEN_SLOT), eq(true))
        verify(iconController).setIcon(eq(ZEN_SLOT), anyInt(), eq("Priority only"))

        zenModeController.setZen(Settings.Global.ZEN_MODE_OFF, null, null)

        verify(iconController).setIconVisibility(eq(ZEN_SLOT), eq(false))
    }

    private fun createAlarmInfo(): AlarmManager.AlarmClockInfo {
        return AlarmManager.AlarmClockInfo(10L, null)
    }
@@ -412,6 +485,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
            privacyItemController,
            privacyLogger,
            fakeConnectedDisplayStateProvider,
            kosmos.zenModeInteractor,
            JavaAdapter(testScope.backgroundScope)
        )
    }
@@ -433,4 +507,51 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
        override val concurrentDisplaysInProgress: Flow<Boolean>
            get() = TODO("Not yet implemented")
    }

    private class FakeZenModeController : ZenModeController {

        private val callbacks = mutableListOf<ZenModeController.Callback>()
        private var zen = Settings.Global.ZEN_MODE_OFF
        private var consolidatedPolicy = NotificationManager.Policy(0, 0, 0)

        override fun addCallback(listener: ZenModeController.Callback) {
            callbacks.add(listener)
        }

        override fun removeCallback(listener: ZenModeController.Callback) {
            callbacks.remove(listener)
        }

        override fun setZen(zen: Int, conditionId: Uri?, reason: String?) {
            this.zen = zen
            callbacks.forEach { it.onZenChanged(zen) }
        }

        override fun getZen(): Int = zen

        override fun getManualRule(): ZenModeConfig.ZenRule = throw NotImplementedError()

        override fun getConfig(): ZenModeConfig = throw NotImplementedError()

        fun setConsolidatedPolicy(policy: NotificationManager.Policy) {
            this.consolidatedPolicy = policy
            callbacks.forEach { it.onConsolidatedPolicyChanged(consolidatedPolicy) }
        }

        override fun getConsolidatedPolicy(): NotificationManager.Policy = consolidatedPolicy

        override fun getNextAlarm() = throw NotImplementedError()

        override fun isZenAvailable() = throw NotImplementedError()

        override fun getEffectsSuppressor() = throw NotImplementedError()

        override fun isCountdownConditionSupported() = throw NotImplementedError()

        override fun getCurrentUser() = throw NotImplementedError()

        override fun isVolumeRestricted() = throw NotImplementedError()

        override fun areNotificationsHiddenInShade() = throw NotImplementedError()
    }
}