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

Commit b388fc17 authored by Angela Wang's avatar Angela Wang
Browse files

Seperated ambient mute state for different devices

Store seperated mute state for each devices instead of having an unified mute state. To reflect the mute icon behavior, if one device is mutable then the icon is mutable(clickable); if all devices are muted then the icon will show muted icon.

Flag: EXEMPT bugfix
Bug: 412968925
Test: manually test with real devices
Test: atest AmbientVolumeLayoutTest
Change-Id: I3750a62a7b15cd346f4af0b19a7a88cc2e412dcd
parent 3aedb245
Loading
Loading
Loading
Loading
+57 −33
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.systemui.accessibility.hearingaid;

import static android.bluetooth.AudioInputControl.MUTE_DISABLED;
import static android.bluetooth.AudioInputControl.MUTE_MUTED;
import static android.bluetooth.AudioInputControl.MUTE_NOT_MUTED;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;

@@ -79,7 +82,6 @@ public class AmbientVolumeLayoutTest extends SysuiTestCase {
        mLayout = new AmbientVolumeLayout(mContext);
        mLayout.setListener(mListener);
        mLayout.setControlExpandable(true);
        mLayout.setMutable(true);

        prepareDevices();
        mLayout.setupSliders(mSideToDeviceMap);
@@ -128,36 +130,7 @@ public class AmbientVolumeLayoutTest extends SysuiTestCase {
    }

    @Test
    public void setMutable_mutable_clickOnMuteIconChangeMuteState() {
        mLayout.setMutable(true);
        mLayout.setMuted(false);

        mVolumeIcon.callOnClick();

        assertThat(mLayout.isMuted()).isTrue();
    }

    @Test
    public void setMutable_notMutable_clickOnMuteIconWontChangeMuteState() {
        mLayout.setMutable(false);
        mLayout.setMuted(false);

        mVolumeIcon.callOnClick();

        assertThat(mLayout.isMuted()).isFalse();
    }

    @Test
    public void updateLayout_mute_volumeIconIsCorrect() {
        mLayout.setMuted(true);
        mLayout.updateLayout();

        assertThat(mVolumeIcon.getDrawable().getLevel()).isEqualTo(0);
    }

    @Test
    public void updateLayout_unmuteAndExpanded_volumeIconIsCorrect() {
        mLayout.setMuted(false);
    public void updateLayout_expanded_volumeIconIsCorrect() {
        mLayout.setControlExpanded(true);
        mLayout.updateLayout();

@@ -166,8 +139,7 @@ public class AmbientVolumeLayoutTest extends SysuiTestCase {
    }

    @Test
    public void updateLayout_unmuteAndNotExpanded_volumeIconIsCorrect() {
        mLayout.setMuted(false);
    public void updateLayout_notExpanded_volumeIconIsCorrect() {
        mLayout.setControlExpanded(false);
        mLayout.updateLayout();

@@ -194,6 +166,58 @@ public class AmbientVolumeLayoutTest extends SysuiTestCase {
        assertThat(mVolumeIcon.getDrawable().getLevel()).isEqualTo(expectedLevel);
    }

    @Test
    public void isMutable_bothSideNotMutable_returnFalse() {
        mLayout.setSliderMuteState(SIDE_LEFT, MUTE_DISABLED);
        mLayout.setSliderMuteState(SIDE_RIGHT, MUTE_DISABLED);

        assertThat(mLayout.isMutable()).isFalse();
    }

    @Test
    public void isMutable_oneSideMutable_returnTrue() {
        mLayout.setSliderMuteState(SIDE_LEFT, MUTE_DISABLED);
        mLayout.setSliderMuteState(SIDE_RIGHT, MUTE_NOT_MUTED);

        assertThat(mLayout.isMutable()).isTrue();
    }

    @Test
    public void isMuted_bothSideMuted_returnTrue() {
        mLayout.setSliderMuteState(SIDE_LEFT, MUTE_MUTED);
        mLayout.setSliderMuteState(SIDE_RIGHT, MUTE_MUTED);

        assertThat(mLayout.isMuted()).isTrue();
    }

    @Test
    public void isMuted_oneSideNotMuted_returnFalse() {
        mLayout.setSliderMuteState(SIDE_LEFT, MUTE_MUTED);
        mLayout.setSliderMuteState(SIDE_RIGHT, MUTE_NOT_MUTED);

        assertThat(mLayout.isMuted()).isFalse();
    }

    @Test
    public void setSliderMuteState_muteLeft_volumeIconIsCorrect() {
        mLayout.setControlExpanded(true);
        mLayout.setSliderMuteState(SIDE_LEFT, MUTE_MUTED);
        mLayout.setSliderMuteState(SIDE_RIGHT, MUTE_NOT_MUTED);

        int expectedLevel = calculateVolumeLevel(0, TEST_RIGHT_VOLUME_LEVEL);
        assertThat(mVolumeIcon.getDrawable().getLevel()).isEqualTo(expectedLevel);
    }

    @Test
    public void setSliderMuteState_muteLeftAndRight_volumeIconIsCorrect() {
        mLayout.setControlExpanded(true);
        mLayout.setSliderMuteState(SIDE_LEFT, MUTE_MUTED);
        mLayout.setSliderMuteState(SIDE_RIGHT, MUTE_MUTED);

        int expectedLevel = calculateVolumeLevel(0, 0);
        assertThat(mVolumeIcon.getDrawable().getLevel()).isEqualTo(expectedLevel);
    }

    private int calculateVolumeLevel(int left, int right) {
        return left * 5 + right;
    }
+78 −53
Original line number Diff line number Diff line
@@ -16,11 +16,16 @@

package com.android.systemui.accessibility.hearingaid;

import static android.bluetooth.AudioInputControl.MUTE_DISABLED;
import static android.bluetooth.AudioInputControl.MUTE_MUTED;
import static android.bluetooth.AudioInputControl.MUTE_NOT_MUTED;

import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_LEFT;
import static com.android.settingslib.bluetooth.HearingAidInfo.DeviceSide.SIDE_RIGHT;

import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -51,11 +56,11 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi
    private AmbientVolumeUiListener mListener;
    private ImageView mExpandIcon;
    private ImageView mVolumeIcon;
    private final BiMap<Integer, AmbientVolumeSlider> mSideToSliderMap = HashBiMap.create();
    private final Map<Integer, Integer> mSideToMuteStateMap = new ArrayMap<>();

    private boolean mExpandable = true;
    private boolean mExpanded = false;
    private boolean mMutable = false;
    private boolean mMuted = false;
    private final BiMap<Integer, AmbientVolumeSlider> mSideToSliderMap = HashBiMap.create();
    private int mVolumeLevel = AMBIENT_VOLUME_LEVEL_DEFAULT;

    private HearingDevicesUiEventLogger mUiEventLogger;
@@ -101,12 +106,13 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi
        mVolumeIcon = requireViewById(R.id.ambient_volume_icon);
        mVolumeIcon.setImageResource(com.android.settingslib.R.drawable.ic_ambient_volume);
        mVolumeIcon.setOnClickListener(v -> {
            if (!mMutable) {
            if (!isMutable()) {
                return;
            }
            setMuted(!mMuted);
            int updatedMuteState = isMuted() ? MUTE_NOT_MUTED : MUTE_MUTED;
            setSliderMuteState(SIDE_UNIFIED, updatedMuteState);
            if (mUiEventLogger != null) {
                HearingDevicesUiEvent uiEvent = mMuted
                HearingDevicesUiEvent uiEvent = isMuted()
                        ? HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_MUTE
                        : HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_UNMUTE;
                mUiEventLogger.log(uiEvent, mLaunchSourceId);
@@ -119,6 +125,9 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi

        mExpandIcon = requireViewById(R.id.ambient_expand_icon);
        mExpandIcon.setOnClickListener(v -> {
            if (!isControlExpandable()) {
                return;
            }
            setControlExpanded(!mExpanded);
            if (mUiEventLogger != null) {
                HearingDevicesUiEvent uiEvent = mExpanded
@@ -140,12 +149,14 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi

    @Override
    public void setControlExpandable(boolean expandable) {
        if (mExpandable != expandable) {
            mExpandable = expandable;
            if (!mExpandable) {
                setControlExpanded(false);
            }
            updateExpandIcon();
        }
    }

    @Override
    public boolean isControlExpandable() {
@@ -154,13 +165,12 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi

    @Override
    public void setControlExpanded(boolean expanded) {
        if (!mExpandable && expanded) {
            return;
        }
        if (mExpanded != expanded) {
            mExpanded = expanded;
            updateExpandIcon();
            updateLayout();
        }
    }

    @Override
    public boolean isControlExpanded() {
@@ -168,37 +178,51 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi
    }

    @Override
    public void setMutable(boolean mutable) {
        mMutable = mutable;
        if (!mMutable) {
            mVolumeLevel = AMBIENT_VOLUME_LEVEL_DEFAULT;
            setMuted(false);
        }
        updateVolumeIcon();
    public boolean isMutable() {
        return mSideToMuteStateMap.values().stream().anyMatch(mute -> mute != MUTE_DISABLED);
    }

    @Override
    public boolean isMutable() {
        return mMutable;
    public boolean isMuted() {
        return mSideToMuteStateMap.values().stream().allMatch(mute -> mute == MUTE_MUTED);
    }

    @Override
    public void setMuted(boolean muted) {
        if (!mMutable && muted) {
            return;
    public void setSliderMuteState(int side, int muteState) {
        if (side == SIDE_UNIFIED) {
            // propagate the mute state to all other sliders
            mSideToSliderMap.keySet().forEach(s -> {
                if (s != SIDE_UNIFIED) {
                    setSliderMuteState(s, muteState);
                }
        mMuted = muted;
        if (mMutable && mMuted) {
            for (AmbientVolumeSlider slider : mSideToSliderMap.values()) {
            });
        } else {
            AmbientVolumeSlider slider = mSideToSliderMap.get(side);
            if (slider != null) {
                mSideToMuteStateMap.put(side, muteState);
                if (muteState == MUTE_MUTED) {
                    slider.setValue(slider.getMin());
                }
                AmbientVolumeSlider unifiedSlider = mSideToSliderMap.get(SIDE_UNIFIED);
                if (isMuted() && unifiedSlider != null) {
                    unifiedSlider.setValue(unifiedSlider.getMin());
                }
                updateVolumeLevel();
            }
        }
        updateVolumeIcon();
    }

    @Override
    public boolean isMuted() {
        return mMuted;
    public int getSliderMuteState(int side) {
        if (side == SIDE_UNIFIED) {
            if (!isMutable()) {
                return MUTE_DISABLED;
            } else {
                return isMuted() ? MUTE_MUTED : MUTE_NOT_MUTED;
            }
        } else {
            return mSideToMuteStateMap.getOrDefault(side, MUTE_DISABLED);
        }
    }

    @Override
@@ -225,19 +249,22 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi
    }

    @Override
    public void setSliderEnabled(int side, boolean enabled) {
    public void setSliderValue(int side, int value) {
        AmbientVolumeSlider slider = mSideToSliderMap.get(side);
        if (slider != null && slider.isEnabled() != enabled) {
            slider.setEnabled(enabled);
            updateLayout();
        if (slider != null && slider.getValue() != value) {
            slider.setValue(value);
            updateVolumeLevel();
        }
    }

    @Override
    public void setSliderValue(int side, int value) {
    public void setSliderEnabled(int side, boolean enabled) {
        AmbientVolumeSlider slider = mSideToSliderMap.get(side);
        if (slider != null && slider.getValue() != value) {
            slider.setValue(value);
        if (slider != null && slider.isEnabled() != enabled) {
            slider.setEnabled(enabled);
            if (!enabled) {
                slider.setValue(slider.getMin());
            }
            updateVolumeLevel();
        }
    }
@@ -259,9 +286,6 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi
            } else {
                slider.setVisibility(mExpanded ? VISIBLE : GONE);
            }
            if (!slider.isEnabled()) {
                slider.setValue(slider.getMin());
            }
        });
        updateVolumeLevel();
    }
@@ -273,7 +297,7 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi

    private void updateVolumeLevel() {
        int leftLevel, rightLevel;
        if (mExpanded) {
        if (isControlExpanded()) {
            leftLevel = getVolumeLevel(SIDE_LEFT);
            rightLevel = getVolumeLevel(SIDE_RIGHT);
        } else {
@@ -295,10 +319,11 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi
    }

    private void updateExpandIcon() {
        mExpandIcon.setVisibility(mExpandable ? VISIBLE : GONE);
        mExpandIcon.setRotation(mExpanded ? ROTATION_EXPANDED : ROTATION_COLLAPSED);
        if (mExpandable) {
            final int stringRes = mExpanded ? R.string.hearing_devices_ambient_collapse_controls
        mExpandIcon.setVisibility(isControlExpandable() ? VISIBLE : GONE);
        mExpandIcon.setRotation(isControlExpanded() ? ROTATION_EXPANDED : ROTATION_COLLAPSED);
        if (isControlExpandable()) {
            final int stringRes = isControlExpanded()
                    ? R.string.hearing_devices_ambient_collapse_controls
                    : R.string.hearing_devices_ambient_expand_controls;
            mExpandIcon.setContentDescription(mContext.getString(stringRes));
        } else {
@@ -307,9 +332,9 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi
    }

    private void updateVolumeIcon() {
        mVolumeIcon.setImageLevel(mMuted ? 0 : mVolumeLevel);
        if (mMutable) {
            final int stringRes = mMuted ? R.string.hearing_devices_ambient_unmute
        mVolumeIcon.setImageLevel(mVolumeLevel);
        if (isMutable()) {
            final int stringRes = isMuted() ? R.string.hearing_devices_ambient_unmute
                    : R.string.hearing_devices_ambient_mute;
            mVolumeIcon.setContentDescription(mContext.getString(stringRes));
            mVolumeIcon.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);