diff options
| author | 2023-05-08 18:53:32 +0000 | |
|---|---|---|
| committer | 2023-05-08 18:53:32 +0000 | |
| commit | 83d075129866ff3074bdf073ee30f30e6bcec245 (patch) | |
| tree | 93d2099cd297c82510c96c1dbb9198b1f744d6a4 | |
| parent | 22490466ca81dde56cd031834787cb0fb9a20fbb (diff) | |
| parent | 76ace72c68d4f4e3e4f1b5e1e9a8a992357cef99 (diff) | |
Merge "Move volume dialog away from the hinge" into udc-dev
4 files changed, 259 insertions, 17 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java index fc0033d71844..2d1e622fbdce 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/Events.java +++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java @@ -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; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 91078dc65477..f893cf4694f9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -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, @@ -365,6 +383,25 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, } /** + * 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. * If ringer and notification are separated, then use generic speaker icons. @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java index 14d3ca334073..bb04f82fcffa 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -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); diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index e06b43ac5aea..45a37cffa588 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -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); } /* |