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

Commit 188126dc authored by Matías Hernández's avatar Matías Hernández
Browse files

Fix size and color of priority modes icons in status bar

* Allow StatusBarIcons to specify whether they want a "fixed" size instead of the variable width they normally have. This also affects their scaling calculations. This is mostly for app-provided icons which may have nonstandard sizes and proportions.
* Create a new drawable from constant state so that theming isn't shared with the other usages.

Additionally: Add a flow to ZenModeInteractor with the main active mode name and icon exclusively. While the QS tile cares about which other modes are active, neither status bar nor Smartspace do, which means we can use a flow that doesn't emit when more modes are activated (and thus avoid wasted work).

Bug: 360399800
Test: coming
Flag: android.app.modes_ui
Change-Id: I6a17d5a49f76169f15efc89432e1ce0f993d040e
parent 308bfd78
Loading
Loading
Loading
Loading
+25 −2
Original line number Diff line number Diff line
@@ -51,6 +51,19 @@ public class StatusBarIcon implements Parcelable {
        ResourceIcon
    }

    public enum Shape {
        /**
         * Icon view should use WRAP_CONTENT -- so that the horizontal space occupied depends on the
         * icon's shape (skinny/fat icons take less/more). Most icons will want to use this option
         * for a nicer-looking overall spacing in the status bar, as long as the icon is "known"
         * (i.e. not coming from a 3P package).
         */
        WRAP_CONTENT,

        /** Icon should always be displayed in a space as wide as the status bar is tall. */
        FIXED_SPACE,
    }

    public UserHandle user;
    public String pkg;
    public Icon icon;
@@ -59,6 +72,7 @@ public class StatusBarIcon implements Parcelable {
    public int number;
    public CharSequence contentDescription;
    public Type type;
    public Shape shape;

    /**
     * Optional {@link Drawable} corresponding to {@link #icon}. This field is not parcelable, so
@@ -68,7 +82,7 @@ public class StatusBarIcon implements Parcelable {
    @Nullable public Drawable preloadedIcon;

    public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number,
            CharSequence contentDescription, Type type) {
            CharSequence contentDescription, Type type, Shape shape) {
        if (icon.getType() == Icon.TYPE_RESOURCE
                && TextUtils.isEmpty(icon.getResPackage())) {
            // This is an odd situation where someone's managed to hand us an icon without a
@@ -83,6 +97,13 @@ public class StatusBarIcon implements Parcelable {
        this.number = number;
        this.contentDescription = contentDescription;
        this.type = type;
        this.shape = shape;
    }

    public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number,
            CharSequence contentDescription, Type type) {
        this(user, resPackage, icon, iconLevel, number, contentDescription, type,
                Shape.WRAP_CONTENT);
    }

    public StatusBarIcon(String iconPackage, UserHandle user,
@@ -107,7 +128,7 @@ public class StatusBarIcon implements Parcelable {
    @Override
    public StatusBarIcon clone() {
        StatusBarIcon that = new StatusBarIcon(this.user, this.pkg, this.icon,
                this.iconLevel, this.number, this.contentDescription, this.type);
                this.iconLevel, this.number, this.contentDescription, this.type, this.shape);
        that.visible = this.visible;
        that.preloadedIcon = this.preloadedIcon;
        return that;
@@ -129,6 +150,7 @@ public class StatusBarIcon implements Parcelable {
        this.number = in.readInt();
        this.contentDescription = in.readCharSequence();
        this.type = Type.valueOf(in.readString());
        this.shape = Shape.valueOf(in.readString());
    }

    public void writeToParcel(Parcel out, int flags) {
@@ -140,6 +162,7 @@ public class StatusBarIcon implements Parcelable {
        out.writeInt(this.number);
        out.writeCharSequence(this.contentDescription);
        out.writeString(this.type.name());
        out.writeString(this.shape.name());
    }

    public int describeContents() {
+7 −5
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;

import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.UserHandle;

@@ -69,22 +70,22 @@ public class StatusBarIconTest {
        assertThat(copy.preloadedIcon).isEqualTo(original.preloadedIcon);
    }


    private static StatusBarIcon newStatusBarIcon() {
        final UserHandle dummyUserHandle = UserHandle.of(100);
        final String dummyIconPackageName = "com.android.internal.statusbar.test";
        final int dummyIconId = 123;
        final Icon dummyIcon = Icon.createWithResource(dummyIconPackageName, 123);
        final int dummyIconLevel = 1;
        final int dummyIconNumber = 2;
        final CharSequence dummyIconContentDescription = "dummyIcon";
        return new StatusBarIcon(
                dummyIconPackageName,
                dummyUserHandle,
                dummyIconId,
                dummyIconPackageName,
                dummyIcon,
                dummyIconLevel,
                dummyIconNumber,
                dummyIconContentDescription,
                StatusBarIcon.Type.SystemIcon);
                StatusBarIcon.Type.SystemIcon,
                StatusBarIcon.Shape.FIXED_SPACE);
    }

    private static void assertSerializableFieldsEqual(StatusBarIcon copy, StatusBarIcon original) {
@@ -96,6 +97,7 @@ public class StatusBarIconTest {
        assertThat(copy.number).isEqualTo(original.number);
        assertThat(copy.contentDescription).isEqualTo(original.contentDescription);
        assertThat(copy.type).isEqualTo(original.type);
        assertThat(copy.shape).isEqualTo(original.shape);
    }

    private static StatusBarIcon parcelAndUnparcel(StatusBarIcon original) {
+54 −0
Original line number Diff line number Diff line
@@ -22,8 +22,10 @@ import android.provider.Settings
import android.provider.Settings.Secure.ZEN_DURATION
import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
import android.service.notification.SystemZenRules
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.settingslib.notification.data.repository.updateNotificationPolicy
import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.systemui.SysuiTestCase
@@ -253,4 +255,56 @@ class ZenModeInteractorTest : SysuiTestCase() {
            assertThat(activeModes?.modeNames).hasSize(0)
            assertThat(activeModes?.mainMode).isNull()
        }

    @Test
    fun mainActiveMode_flows() =
        testScope.runTest {
            val mainActiveMode by collectLastValue(underTest.mainActiveMode)

            zenModeRepository.addModes(
                listOf(
                    TestModeBuilder()
                        .setId("Bedtime")
                        .setName("Mode Bedtime")
                        .setType(AutomaticZenRule.TYPE_BEDTIME)
                        .setActive(false)
                        .setPackage(mContext.packageName)
                        .setIconResId(R.drawable.ic_zen_mode_type_bedtime)
                        .build(),
                    TestModeBuilder()
                        .setId("Other")
                        .setName("Mode Other")
                        .setType(AutomaticZenRule.TYPE_OTHER)
                        .setActive(false)
                        .setPackage(SystemZenRules.PACKAGE_ANDROID)
                        .setIconResId(R.drawable.ic_zen_mode_type_other)
                        .build(),
                )
            )

            runCurrent()
            assertThat(mainActiveMode).isNull()

            zenModeRepository.activateMode("Other")
            runCurrent()
            assertThat(mainActiveMode?.name).isEqualTo("Mode Other")
            assertThat(mainActiveMode?.icon?.key?.resId)
                .isEqualTo(R.drawable.ic_zen_mode_type_other)

            zenModeRepository.activateMode("Bedtime")
            runCurrent()
            assertThat(mainActiveMode?.name).isEqualTo("Mode Bedtime")
            assertThat(mainActiveMode?.icon?.key?.resId)
                .isEqualTo(R.drawable.ic_zen_mode_type_bedtime)

            zenModeRepository.deactivateMode("Other")
            runCurrent()
            assertThat(mainActiveMode?.name).isEqualTo("Mode Bedtime")
            assertThat(mainActiveMode?.icon?.key?.resId)
                .isEqualTo(R.drawable.ic_zen_mode_type_bedtime)

            zenModeRepository.deactivateMode("Bedtime")
            runCurrent()
            assertThat(mainActiveMode).isNull()
        }
}
+18 −7
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import androidx.core.graphics.ColorUtils;
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIcon.Shape;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Flags;
import com.android.systemui.res.R;
@@ -211,16 +212,19 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
    /** Should always be preceded by {@link #reloadDimens()} */
    @VisibleForTesting
    public void maybeUpdateIconScaleDimens() {
        // We do not resize and scale system icons (on the right), only notification icons (on the
        // left).
        if (isNotification()) {
            updateIconScaleForNotifications();
        // We scale notification icons (on the left) plus icons on the right that explicitly
        // want FIXED_SPACE.
        boolean useNonSystemIconScaling = isNotification()
                || (usesModeIcons() && mIcon != null && mIcon.shape == Shape.FIXED_SPACE);

        if (useNonSystemIconScaling) {
            updateIconScaleForNonSystemIcons();
        } else {
            updateIconScaleForSystemIcons();
        }
    }

    private void updateIconScaleForNotifications() {
    private void updateIconScaleForNonSystemIcons() {
        float iconScale;
        // we need to scale the image size to be same as the original size
        // (fit mOriginalStatusBarIconSize), then we can scale it with mScaleToFitNewIconSize
@@ -411,7 +415,9 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
        if (!levelEquals) {
            setImageLevel(icon.iconLevel);
        }

        if (usesModeIcons()) {
            setScaleType(icon.shape == Shape.FIXED_SPACE ? ScaleType.FIT_CENTER : ScaleType.CENTER);
        }
        if (!visibilityEquals) {
            setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE);
        }
@@ -501,7 +507,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
    @Nullable
    private Drawable loadDrawable(Context context, StatusBarIcon statusBarIcon) {
        if (usesModeIcons() && statusBarIcon.preloadedIcon != null) {
            Drawable.ConstantState cached = statusBarIcon.preloadedIcon.getConstantState();
            if (cached != null) {
                return cached.newDrawable(mContext.getResources()).mutate();
            } else {
                return statusBarIcon.preloadedIcon.mutate();
            }
        } else {
            int userId = statusBarIcon.user.getIdentifier();
            if (userId == UserHandle.USER_ALL) {
+10 −9
Original line number Diff line number Diff line
@@ -42,9 +42,9 @@ import android.text.format.DateFormat;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.lifecycle.Observer;

import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.Flags;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -80,7 +80,6 @@ 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.statusbar.policy.domain.model.ActiveZenModes;
import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.kotlin.JavaAdapter;
@@ -364,8 +363,8 @@ public class PhoneStatusBarPolicy
        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.getActiveModes(),
                    this::onActiveModesChanged);
            mJavaAdapter.alwaysCollectFlow(mZenModeInteractor.getMainActiveMode(),
                    this::onMainActiveModeChanged);
        }
        mZenController.addCallback(mZenControllerCallback);
        if (!Flags.statusBarScreenSharingChips()) {
@@ -397,21 +396,23 @@ public class PhoneStatusBarPolicy
                () -> mResources.getString(R.string.accessibility_managed_profile));
    }

    private void onActiveModesChanged(@NonNull ActiveZenModes activeModes) {
    private void onMainActiveModeChanged(@Nullable ZenModeInfo mainActiveMode) {
        if (!usesModeIcons()) {
            Log.wtf(TAG, "onActiveModeChanged shouldn't be called if MODES_UI_ICONS is disabled");
            Log.wtf(TAG, "onMainActiveModeChanged shouldn't run if MODES_UI_ICONS is disabled");
            return;
        }

        ZenModeInfo mainActiveMode = activeModes.getMainMode();
        boolean visible = mainActiveMode != null;

        if (visible) {
            // Shape=FIXED_SPACE because mode icons can be from 3P packages and may not be square;
            // we don't want to allow apps to set incredibly wide icons and take up too much space
            // in the status bar.
            mIconController.setResourceIcon(mSlotZen,
                    mainActiveMode.getIcon().key().resPackage(),
                    mainActiveMode.getIcon().key().resId(),
                    mainActiveMode.getIcon().drawable(),
                    mainActiveMode.getName());
                    mainActiveMode.getName(),
                    StatusBarIcon.Shape.FIXED_SPACE);
        }
        if (visible != mZenVisible) {
            mIconController.setIconVisibility(mSlotZen, visible);
Loading