diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 349f3684659cd..87b2697611d20 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.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; import static com.android.systemui.volume.Events.DISMISS_REASON_POSTURE_CHANGED; import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED; @@ -82,6 +83,7 @@ import android.util.Slog; import android.util.SparseBooleanArray; import android.view.ContextThemeWrapper; import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; import android.view.View.AccessibilityDelegate; @@ -120,6 +122,7 @@ import com.android.systemui.Dumpable; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.VolumeDialog; @@ -300,6 +303,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, private final DevicePostureController mDevicePostureController; private @DevicePostureController.DevicePostureInt int mDevicePosture; private int mOrientation; + private final FeatureFlags mFeatureFlags; public VolumeDialogImpl( Context context, @@ -315,7 +319,9 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, CsdWarningDialog.Factory csdWarningDialogFactory, DevicePostureController devicePostureController, Looper looper, - DumpManager dumpManager) { + DumpManager dumpManager, + FeatureFlags featureFlags) { + mFeatureFlags = featureFlags; mContext = new ContextThemeWrapper(context, R.style.volume_dialog_theme); mHandler = new H(looper); @@ -1319,12 +1325,14 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, private void provideTouchFeedbackH(int newRingerMode) { VibrationEffect effect = null; + int hapticConstant = HapticFeedbackConstants.NO_HAPTICS; switch (newRingerMode) { case RINGER_MODE_NORMAL: mController.scheduleTouchFeedback(); break; case RINGER_MODE_SILENT: effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + hapticConstant = HapticFeedbackConstants.TOGGLE_OFF; break; case RINGER_MODE_VIBRATE: // Feedback handled by onStateChange, for feedback both when user toggles @@ -1332,8 +1340,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, break; default: effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); + hapticConstant = HapticFeedbackConstants.TOGGLE_ON; } - if (effect != null) { + if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { + mDialogView.performHapticFeedback(hapticConstant); + } else if (effect != null) { mController.vibrate(effect); } } @@ -1770,7 +1781,22 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, && mState.ringerModeInternal != -1 && mState.ringerModeInternal != state.ringerModeInternal && state.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) { - mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK)); + + if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { + if (mShowing) { + // The dialog view is responsible for triggering haptics in the oneway API + mDialogView.performHapticFeedback(HapticFeedbackConstants.TOGGLE_ON); + } + /* + TODO(b/290642122): If the dialog is not showing, we have the case where haptics is + enabled by dragging the volume slider of Settings to a value of 0. This must be + handled by view Slices in Settings whilst using the performHapticFeedback API. + */ + + } else { + // Old behavior only active if the oneway API is not used. + mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK)); + } } mState = state; mDynamic.clear(); 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 d0edc6e7ce4c2..cc9f3e14216ef 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -22,6 +22,7 @@ import android.os.Looper; import com.android.internal.jank.InteractionJankMonitor; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.VolumeDialog; @@ -61,7 +62,8 @@ public interface VolumeModule { InteractionJankMonitor interactionJankMonitor, CsdWarningDialog.Factory csdFactory, DevicePostureController devicePostureController, - DumpManager dumpManager) { + DumpManager dumpManager, + FeatureFlags featureFlags) { VolumeDialogImpl impl = new VolumeDialogImpl( context, volumeDialogController, @@ -76,7 +78,8 @@ public interface VolumeModule { csdFactory, devicePostureController, Looper.getMainLooper(), - dumpManager); + dumpManager, + featureFlags); impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false); impl.setAutomute(true); impl.setSilentMode(false); 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 0c77529377aba..c81910855f789 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -16,6 +16,7 @@ package com.android.systemui.volume; +import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; 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; @@ -51,6 +52,7 @@ import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.VolumeDialogController; @@ -117,6 +119,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { } }; + private FakeFeatureFlags mFeatureFlags; + @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); @@ -132,6 +136,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { mConfigurationController = new FakeConfigurationController(); + mFeatureFlags = new FakeFeatureFlags(); + mDialog = new VolumeDialogImpl( getContext(), mVolumeDialogController, @@ -146,7 +152,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { mCsdWarningDialogFactory, mPostureController, mTestableLooper.getLooper(), - mDumpManager); + mDumpManager, + mFeatureFlags); mDialog.init(0, null); State state = createShellState(); mDialog.onStateChangedH(state); @@ -254,6 +261,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testVibrateOnRingerChangedToVibrate() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); final State initialSilentState = new State(); initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT; @@ -273,8 +281,31 @@ public class VolumeDialogImplTest extends SysuiTestCase { verify(mVolumeDialogController).vibrate(any()); } + @Test + public void testControllerDoesNotVibrateOnRingerChangedToVibrate_OnewayAPI_On() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); + final State initialSilentState = new State(); + initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT; + + final State vibrateState = new State(); + vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; + + // change ringer to silent + mDialog.onStateChangedH(initialSilentState); + + // expected: shouldn't call vibrate yet + verify(mVolumeDialogController, never()).vibrate(any()); + + // changed ringer to vibrate + mDialog.onStateChangedH(vibrateState); + + // expected: vibrate method of controller is not used + verify(mVolumeDialogController, never()).vibrate(any()); + } + @Test public void testNoVibrateOnRingerInitialization() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); final State initialUnsetState = new State(); initialUnsetState.ringerModeInternal = -1; @@ -291,8 +322,43 @@ public class VolumeDialogImplTest extends SysuiTestCase { verify(mVolumeDialogController, never()).vibrate(any()); } + @Test + public void testControllerDoesNotVibrateOnRingerInitialization_OnewayAPI_On() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); + final State initialUnsetState = new State(); + initialUnsetState.ringerModeInternal = -1; + + // ringer not initialized yet: + mDialog.onStateChangedH(initialUnsetState); + + final State vibrateState = new State(); + vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; + + // changed ringer to vibrate + mDialog.onStateChangedH(vibrateState); + + // shouldn't call vibrate on the controller either + verify(mVolumeDialogController, never()).vibrate(any()); + } + @Test public void testSelectVibrateFromDrawer() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); + final State initialUnsetState = new State(); + initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; + mDialog.onStateChangedH(initialUnsetState); + + mActiveRinger.performClick(); + mDrawerVibrate.performClick(); + + // Make sure we've actually changed the ringer mode. + verify(mVolumeDialogController, times(1)).setRingerMode( + AudioManager.RINGER_MODE_VIBRATE, false); + } + + @Test + public void testSelectVibrateFromDrawer_OnewayAPI_On() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); final State initialUnsetState = new State(); initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; mDialog.onStateChangedH(initialUnsetState); @@ -307,6 +373,22 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectMuteFromDrawer() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); + final State initialUnsetState = new State(); + initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; + mDialog.onStateChangedH(initialUnsetState); + + mActiveRinger.performClick(); + mDrawerMute.performClick(); + + // Make sure we've actually changed the ringer mode. + verify(mVolumeDialogController, times(1)).setRingerMode( + AudioManager.RINGER_MODE_SILENT, false); + } + + @Test + public void testSelectMuteFromDrawer_OnewayAPI_On() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); final State initialUnsetState = new State(); initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; mDialog.onStateChangedH(initialUnsetState); @@ -321,6 +403,22 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectNormalFromDrawer() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); + final State initialUnsetState = new State(); + initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; + mDialog.onStateChangedH(initialUnsetState); + + mActiveRinger.performClick(); + mDrawerNormal.performClick(); + + // Make sure we've actually changed the ringer mode. + verify(mVolumeDialogController, times(1)).setRingerMode( + AudioManager.RINGER_MODE_NORMAL, false); + } + + @Test + public void testSelectNormalFromDrawer_OnewayAPI_On() { + mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); final State initialUnsetState = new State(); initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; mDialog.onStateChangedH(initialUnsetState); @@ -383,7 +481,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), - mDumpManager + mDumpManager, + mFeatureFlags ); dialog.init(0 , null); @@ -423,7 +522,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), - mDumpManager + mDumpManager, + mFeatureFlags ); dialog.init(0, null); @@ -462,7 +562,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), - mDumpManager + mDumpManager, + mFeatureFlags ); dialog.init(0, null); @@ -503,7 +604,9 @@ public class VolumeDialogImplTest extends SysuiTestCase { mCsdWarningDialogFactory, mPostureController, mTestableLooper.getLooper(), - mDumpManager); + mDumpManager, + mFeatureFlags + ); dialog.init(0, null); verify(mPostureController, never()).removeCallback(any());