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

Commit 83d07512 authored by Behnam Heydarshahi's avatar Behnam Heydarshahi Committed by Android (Google) Code Review
Browse files

Merge "Move volume dialog away from the hinge" into udc-dev

parents 22490466 76ace72c
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -98,6 +98,8 @@ public class Events {
    public static final int DISMISS_REASON_OUTPUT_CHOOSER = 8;
    public static final int DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED = 9;
    public static final int DISMISS_REASON_CSD_WARNING_TIMEOUT = 10;
    public static final int DISMISS_REASON_POSTURE_CHANGED = 11;

    public static final String[] DISMISS_REASONS = {
            "unknown",
            "touch_outside",
@@ -109,7 +111,8 @@ public class Events {
            "a11y_stream_changed",
            "output_chooser",
            "usb_temperature_below_threshold",
            "csd_warning_timeout"
            "csd_warning_timeout",
            "posture_changed"
    };

    public static final int SHOW_REASON_UNKNOWN = 0;
+65 −11
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

import static com.android.internal.jank.InteractionJankMonitor.CUJ_VOLUME_CONTROL;
import static com.android.internal.jank.InteractionJankMonitor.Configuration.Builder;
import static com.android.systemui.volume.Events.DISMISS_REASON_POSTURE_CHANGED;
import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;

import android.animation.Animator;
@@ -129,6 +130,7 @@ import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.plugins.VolumeDialogController.StreamState;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.AlphaTintDrawableWrapper;
import com.android.systemui.util.DeviceConfigProxy;
@@ -184,7 +186,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
    private final boolean mChangeVolumeRowTintWhenInactive;

    private final Context mContext;
    private final H mHandler = new H();
    private final H mHandler;
    private final VolumeDialogController mController;
    private final DeviceProvisionedController mDeviceProvisionedController;
    private final Region mTouchableRegion = new Region();
@@ -259,16 +261,13 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
    private final AccessibilityManagerWrapper mAccessibilityMgr;
    private final Object mSafetyWarningLock = new Object();
    private final Accessibility mAccessibility = new Accessibility();

    private final ConfigurationController mConfigurationController;
    private final MediaOutputDialogFactory mMediaOutputDialogFactory;
    private final VolumePanelFactory mVolumePanelFactory;
    private final CsdWarningDialog.Factory mCsdWarningDialogFactory;
    private final ActivityStarter mActivityStarter;

    private boolean mShowing;
    private boolean mShowA11yStream;

    private int mActiveStream;
    private int mPrevActiveStream;
    private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
@@ -300,6 +299,12 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
    @VisibleForTesting
    int mVolumeRingerMuteIconDrawableId;

    private int mOriginalGravity;
    private final DevicePostureController.Callback mDevicePostureControllerCallback;
    private final DevicePostureController mDevicePostureController;
    private @DevicePostureController.DevicePostureInt int mDevicePosture;
    private int mOrientation;

    public VolumeDialogImpl(
            Context context,
            VolumeDialogController volumeDialogController,
@@ -313,9 +318,12 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
            DeviceConfigProxy deviceConfigProxy,
            Executor executor,
            CsdWarningDialog.Factory csdWarningDialogFactory,
            DevicePostureController devicePostureController,
            Looper looper,
            DumpManager dumpManager) {
        mContext =
                new ContextThemeWrapper(context, R.style.volume_dialog_theme);
        mHandler = new H(looper);
        mController = volumeDialogController;
        mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
@@ -357,6 +365,16 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,

        initDimens();

        mOrientation = mContext.getResources().getConfiguration().orientation;
        mDevicePostureController = devicePostureController;
        if (mDevicePostureController != null) {
            int initialPosture = mDevicePostureController.getDevicePosture();
            mDevicePosture = initialPosture;
            mDevicePostureControllerCallback = this::onPostureChanged;
        } else {
            mDevicePostureControllerCallback = null;
        }

        mDeviceConfigProxy = deviceConfigProxy;
        mExecutor = executor;
        mSeparateNotification = mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
@@ -364,6 +382,25 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
        updateRingerModeIconSet();
    }

    /**
     * Adjust the dialog location on the screen in order to avoid drawing on the hinge.
     */
    private void adjustPositionOnScreen() {
        final boolean isPortrait = mOrientation == Configuration.ORIENTATION_PORTRAIT;
        final boolean isHalfOpen =
                mDevicePosture == DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
        final boolean isTabletop = isPortrait && isHalfOpen;
        WindowManager.LayoutParams lp =  mWindow.getAttributes();
        int gravity = isTabletop ? (mOriginalGravity | Gravity.TOP) : mOriginalGravity;
        mWindowGravity = Gravity.getAbsoluteGravity(gravity,
                mContext.getResources().getConfiguration().getLayoutDirection());
        lp.gravity = mWindowGravity;
    }

    @VisibleForTesting int getWindowGravity() {
        return mWindowGravity;
    }

    /**
     * If ringer and notification are the same stream (T and earlier), use notification-like bell
     * icon set.
@@ -419,6 +456,10 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,

        mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
                mExecutor, this::onDeviceConfigChange);

        if (mDevicePostureController != null) {
            mDevicePostureController.addCallback(mDevicePostureControllerCallback);
        }
    }

    @Override
@@ -427,6 +468,9 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
        mHandler.removeCallbacksAndMessages(null);
        mConfigurationController.removeCallback(this);
        mDeviceConfigProxy.removeOnPropertiesChangedListener(this::onDeviceConfigChange);
        if (mDevicePostureController != null) {
            mDevicePostureController.removeCallback(mDevicePostureControllerCallback);
        }
    }

    /**
@@ -441,7 +485,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
                mSeparateNotification = newVal;
                updateRingerModeIconSet();
                updateRingRowIcon();

            }
        }
    }
@@ -500,7 +543,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,

    private void initDialog(int lockTaskModeState) {
        mDialog = new CustomDialog(mContext);

        initDimens();

        mConfigurableTexts = new ConfigurableTexts(mContext);
@@ -524,14 +566,13 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
        lp.setTitle(VolumeDialogImpl.class.getSimpleName());
        lp.windowAnimations = -1;

        mWindowGravity = Gravity.getAbsoluteGravity(
                mContext.getResources().getInteger(R.integer.volume_dialog_gravity),
        mOriginalGravity = mContext.getResources().getInteger(R.integer.volume_dialog_gravity);
        mWindowGravity = Gravity.getAbsoluteGravity(mOriginalGravity,
                mContext.getResources().getConfiguration().getLayoutDirection());
        lp.gravity = mWindowGravity;

        mWindow.setAttributes(lp);
        mWindow.setLayout(WRAP_CONTENT, WRAP_CONTENT);

        mDialog.setContentView(R.layout.volume_dialog);
        mDialogView = mDialog.findViewById(R.id.volume_dialog);
        mDialogView.setAlpha(0);
@@ -1539,8 +1580,10 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
            animator.translationX(
                    (isWindowGravityLeft() ? -1 : 1) * mDialogView.getWidth() / 2.0f);
        }

        animator.setListener(getJankListener(getDialogView(), TYPE_DISMISS,
                mDialogHideAnimationDurationMs)).start();

        checkODICaptionsTooltip(true);
        synchronized (mSafetyWarningLock) {
            if (mSafetyWarning != null) {
@@ -2237,6 +2280,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
        mTopContainer.setBackground(background);
    }

    @Override
    public void onConfigChanged(Configuration config) {
        mOrientation = config.orientation;
    }

    private final VolumeDialogController.Callbacks mControllerCallbackH
            = new VolumeDialogController.Callbacks() {
        @Override
@@ -2313,6 +2361,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
        }
    };

    @VisibleForTesting void onPostureChanged(int posture) {
        dismiss(DISMISS_REASON_POSTURE_CHANGED);
        mDevicePosture = posture;
    }

    private final class H extends Handler {
        private static final int SHOW = 1;
        private static final int DISMISS = 2;
@@ -2323,8 +2376,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
        private static final int STATE_CHANGED = 7;
        private static final int CSD_TIMEOUT = 8;

        public H() {
            super(Looper.getMainLooper());
        H(Looper looper) {
            super(looper);
        }

        @Override
@@ -2370,6 +2423,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
        protected void onStart() {
            super.setCanceledOnTouchOutside(true);
            super.onStart();
            adjustPositionOnScreen();
        }

        @Override
+5 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dagger;

import android.content.Context;
import android.media.AudioManager;
import android.os.Looper;

import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.dagger.qualifiers.Main;
@@ -28,6 +29,7 @@ import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.volume.CsdWarningDialog;
@@ -42,7 +44,6 @@ import dagger.Provides;

import java.util.concurrent.Executor;


/** Dagger Module for code in the volume package. */
@Module
public interface VolumeModule {
@@ -65,6 +66,7 @@ public interface VolumeModule {
            DeviceConfigProxy deviceConfigProxy,
            @Main Executor executor,
            CsdWarningDialog.Factory csdFactory,
            DevicePostureController devicePostureController,
            DumpManager dumpManager) {
        VolumeDialogImpl impl = new VolumeDialogImpl(
                context,
@@ -79,6 +81,8 @@ public interface VolumeModule {
                deviceConfigProxy,
                executor,
                csdFactory,
                devicePostureController,
                Looper.getMainLooper(),
                dumpManager);
        impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
        impl.setAutomute(true);
+185 −4
Original line number Diff line number Diff line
@@ -17,22 +17,28 @@
package com.android.systemui.volume;

import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.KeyguardManager;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
@@ -53,7 +59,9 @@ import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -82,6 +90,9 @@ public class VolumeDialogImplTest extends SysuiTestCase {
    View mDrawerNormal;
    private DeviceConfigProxyFake mDeviceConfigProxy;
    private FakeExecutor mExecutor;
    private TestableLooper mTestableLooper;
    private ConfigurationController mConfigurationController;
    private int mOriginalOrientation;

    @Mock
    VolumeDialogController mVolumeDialogController;
@@ -92,8 +103,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
    @Mock
    DeviceProvisionedController mDeviceProvisionedController;
    @Mock
    ConfigurationController mConfigurationController;
    @Mock
    MediaOutputDialogFactory mMediaOutputDialogFactory;
    @Mock
    VolumePanelFactory mVolumePanelFactory;
@@ -104,6 +113,8 @@ public class VolumeDialogImplTest extends SysuiTestCase {
    @Mock
    private DumpManager mDumpManager;
    @Mock CsdWarningDialog mCsdWarningDialog;
    @Mock
    DevicePostureController mPostureController;

    private final CsdWarningDialog.Factory mCsdWarningDialogFactory =
            new CsdWarningDialog.Factory() {
@@ -119,9 +130,17 @@ public class VolumeDialogImplTest extends SysuiTestCase {

        getContext().addMockSystemService(KeyguardManager.class, mKeyguard);

        mTestableLooper = TestableLooper.get(this);
        mDeviceConfigProxy = new DeviceConfigProxyFake();
        mExecutor = new FakeExecutor(new FakeSystemClock());

        when(mPostureController.getDevicePosture())
                .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);

        mOriginalOrientation = mContext.getResources().getConfiguration().orientation;

        mConfigurationController = new FakeConfigurationController();

        mDialog = new VolumeDialogImpl(
                getContext(),
                mVolumeDialogController,
@@ -135,8 +154,9 @@ public class VolumeDialogImplTest extends SysuiTestCase {
                mDeviceConfigProxy,
                mExecutor,
                mCsdWarningDialogFactory,
                mDumpManager
            );
                mPostureController,
                mTestableLooper.getLooper(),
                mDumpManager);
        mDialog.init(0, null);
        State state = createShellState();
        mDialog.onStateChangedH(state);
@@ -227,6 +247,7 @@ public class VolumeDialogImplTest extends SysuiTestCase {
                ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class);
        verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any());
        VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue();

        callbacks.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI);
        verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
                VolumeDialogImpl.DIALOG_SAFETYWARNING_TIMEOUT_MILLIS,
@@ -371,11 +392,171 @@ public class VolumeDialogImplTest extends SysuiTestCase {
        verify(mCsdWarningDialog).show();
    }

    @Test
    public void ifPortraitHalfOpen_drawVerticallyTop() {
        DevicePostureController devicePostureController = mock(DevicePostureController.class);
        when(devicePostureController.getDevicePosture())
                .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);

        VolumeDialogImpl dialog = new VolumeDialogImpl(
                getContext(),
                mVolumeDialogController,
                mAccessibilityMgr,
                mDeviceProvisionedController,
                mConfigurationController,
                mMediaOutputDialogFactory,
                mVolumePanelFactory,
                mActivityStarter,
                mInteractionJankMonitor,
                mDeviceConfigProxy,
                mExecutor,
                mCsdWarningDialogFactory,
                devicePostureController,
                mTestableLooper.getLooper(),
                mDumpManager
        );
        dialog.init(0 , null);

        verify(devicePostureController).addCallback(any());
        dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
        mTestableLooper.processAllMessages(); // let dismiss() finish

        setOrientation(Configuration.ORIENTATION_PORTRAIT);

        // Call show() to trigger layout updates before verifying position
        dialog.show(SHOW_REASON_UNKNOWN);
        mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect

        int gravity = dialog.getWindowGravity();
        assertEquals(Gravity.TOP, gravity & Gravity.VERTICAL_GRAVITY_MASK);
    }

    @Test
    public void ifPortraitAndOpen_drawCenterVertically() {
        DevicePostureController devicePostureController = mock(DevicePostureController.class);
        when(devicePostureController.getDevicePosture())
                .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);

        VolumeDialogImpl dialog = new VolumeDialogImpl(
                getContext(),
                mVolumeDialogController,
                mAccessibilityMgr,
                mDeviceProvisionedController,
                mConfigurationController,
                mMediaOutputDialogFactory,
                mVolumePanelFactory,
                mActivityStarter,
                mInteractionJankMonitor,
                mDeviceConfigProxy,
                mExecutor,
                mCsdWarningDialogFactory,
                devicePostureController,
                mTestableLooper.getLooper(),
                mDumpManager
        );
        dialog.init(0, null);

        verify(devicePostureController).addCallback(any());
        dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED);
        mTestableLooper.processAllMessages(); // let dismiss() finish

        setOrientation(Configuration.ORIENTATION_PORTRAIT);

        dialog.show(SHOW_REASON_UNKNOWN);
        mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect

        int gravity = dialog.getWindowGravity();
        assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK);
    }

    @Test
    public void ifLandscapeAndHalfOpen_drawCenterVertically() {
        DevicePostureController devicePostureController = mock(DevicePostureController.class);
        when(devicePostureController.getDevicePosture())
                .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);

        VolumeDialogImpl dialog = new VolumeDialogImpl(
                getContext(),
                mVolumeDialogController,
                mAccessibilityMgr,
                mDeviceProvisionedController,
                mConfigurationController,
                mMediaOutputDialogFactory,
                mVolumePanelFactory,
                mActivityStarter,
                mInteractionJankMonitor,
                mDeviceConfigProxy,
                mExecutor,
                mCsdWarningDialogFactory,
                devicePostureController,
                mTestableLooper.getLooper(),
                mDumpManager
        );
        dialog.init(0, null);

        verify(devicePostureController).addCallback(any());
        dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
        mTestableLooper.processAllMessages(); // let dismiss() finish

        setOrientation(Configuration.ORIENTATION_LANDSCAPE);

        dialog.show(SHOW_REASON_UNKNOWN);
        mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect

        int gravity = dialog.getWindowGravity();
        assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK);
    }

    @Test
    public void dialogInit_addsPostureControllerCallback() {
        // init is already called in setup
        verify(mPostureController).addCallback(any());
    }

    @Test
    public void dialogDestroy_removesPostureControllerCallback() {
        VolumeDialogImpl dialog = new VolumeDialogImpl(
                getContext(),
                mVolumeDialogController,
                mAccessibilityMgr,
                mDeviceProvisionedController,
                mConfigurationController,
                mMediaOutputDialogFactory,
                mVolumePanelFactory,
                mActivityStarter,
                mInteractionJankMonitor,
                mDeviceConfigProxy,
                mExecutor,
                mCsdWarningDialogFactory,
                mPostureController,
                mTestableLooper.getLooper(),
                mDumpManager
        );
        dialog.init(0, null);

        verify(mPostureController, never()).removeCallback(any());

        dialog.destroy();

        verify(mPostureController).removeCallback(any());
    }

    private void setOrientation(int orientation) {
        Configuration config = new Configuration();
        config.orientation = orientation;
        if (mConfigurationController != null) {
            mConfigurationController.onConfigurationChanged(config);
        }
    }

    @After
    public void teardown() {
        if (mDialog != null) {
            mDialog.clearInternalHandlerAfterTest();
        }
        setOrientation(mOriginalOrientation);
        mTestableLooper.processAllMessages();
        reset(mPostureController);
    }

/*