From f966e40a2ee614f713a5f648f3cc0f3859e4bee4 Mon Sep 17 00:00:00 2001 From: Juan Sebastian Martinez Date: Wed, 19 Jul 2023 15:33:32 +0000 Subject: [PATCH] Using performHapticFeedback on NotificationPanelViewController Migration towards the new one-way API that can trigger haptic feedback from the UI thread. Calls to the vibrate method of the VibratorHelper are replaced by performHapticFeedback with haptic constants that represent interfactions. A GESTURE_START vibration is delivered upon swipe down events and a REJECT vibration when a second tap is required due to falsing. The migration is controlled by a feature flag at the moment. Test: Vibration calls were tested in the NotificationPanelViewControllerWithCoroutinesTest class. Manual verification of vibration was performed in the event of swiping down the notification panel. The vibrations were verified when the feature flag was on and off. Bug: 245528624 Change-Id: I55c3e7a0627358cf5ac0c6e221a6e8bb9a8f8404 --- .../NotificationPanelViewController.java | 27 ++++-- ...onPanelViewControllerWithCoroutinesTest.kt | 85 +++++++++++++++++++ 2 files changed, 105 insertions(+), 7 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index e428976c5ce2f..ed7cbffc880b7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -28,6 +28,7 @@ import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; import static com.android.systemui.classifier.Classifier.GENERIC; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.classifier.Classifier.UNLOCK; +import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll; import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED; @@ -70,6 +71,7 @@ import android.provider.Settings; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.MathUtils; +import android.view.HapticFeedbackConstants; import android.view.InputDevice; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -2632,12 +2634,16 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } if (!mStatusBarStateController.isDozing()) { - mVibratorHelper.vibrate( - Process.myUid(), - mView.getContext().getPackageName(), - ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT, - "falsing-additional-tap-required", - TOUCH_VIBRATION_ATTRIBUTES); + if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { + mVibratorHelper.performHapticFeedback(mView, HapticFeedbackConstants.REJECT); + } else { + mVibratorHelper.vibrate( + Process.myUid(), + mView.getContext().getPackageName(), + ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT, + "falsing-additional-tap-required", + TOUCH_VIBRATION_ATTRIBUTES); + } } } @@ -3504,7 +3510,14 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private void maybeVibrateOnOpening(boolean openingWithTouch) { if (mVibrateOnOpening && mBarState != KEYGUARD && mBarState != SHADE_LOCKED) { if (!openingWithTouch || !mHasVibratedOnOpen) { - mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); + if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { + mVibratorHelper.performHapticFeedback( + mView, + HapticFeedbackConstants.GESTURE_START + ); + } else { + mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); + } mHasVibratedOnOpen = true; mShadeLog.v("Vibrating on opening, mHasVibratedOnOpen=true"); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt index 0c046e93ee20e..c68095ca65a7a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt @@ -16,17 +16,23 @@ package com.android.systemui.shade +import android.os.VibrationEffect import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import android.view.HapticFeedbackConstants import android.view.View import android.view.ViewStub import androidx.test.filters.SmallTest import com.android.internal.util.CollectionUtils import com.android.keyguard.KeyguardClockSwitch.LARGE import com.android.systemui.R +import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.statusbar.StatusBarState.SHADE import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED +import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.Dispatchers @@ -55,6 +61,9 @@ class NotificationPanelViewControllerWithCoroutinesTest : override fun getMainDispatcher() = Dispatchers.Main.immediate + private val ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT = + VibrationEffect.get(VibrationEffect.EFFECT_STRENGTH_MEDIUM, false) + @Test fun testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() = runTest { launch(Dispatchers.Main.immediate) { givenViewAttached() } @@ -147,6 +156,43 @@ class NotificationPanelViewControllerWithCoroutinesTest : advanceUntilIdle() } + @Test + fun doubleTapRequired_onKeyguard_oneWayHapticsDisabled_usesOldVibrate() = runTest { + launch(Dispatchers.Main.immediate) { + whenever(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(false) + val listener = getFalsingTapListener() + mStatusBarStateController.setState(KEYGUARD) + + listener.onAdditionalTapRequired() + val packageName = mView.context.packageName + verify(mKeyguardIndicationController).showTransientIndication(anyInt()) + verify(mVibratorHelper) + .vibrate( + any(), + eq(packageName), + eq(ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT), + eq("falsing-additional-tap-required"), + eq(VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES) + ) + } + advanceUntilIdle() + } + + @Test + fun doubleTapRequired_onKeyguard_oneWayHapticsEnabled_usesPerformHapticFeedback() = runTest { + launch(Dispatchers.Main.immediate) { + whenever(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true) + val listener = getFalsingTapListener() + mStatusBarStateController.setState(KEYGUARD) + + listener.onAdditionalTapRequired() + verify(mKeyguardIndicationController).showTransientIndication(anyInt()) + verify(mVibratorHelper) + .performHapticFeedback(eq(mView), eq(HapticFeedbackConstants.REJECT)) + } + advanceUntilIdle() + } + @Test fun testDoubleTapRequired_ShadeLocked() = runTest { launch(Dispatchers.Main.immediate) { @@ -160,6 +206,45 @@ class NotificationPanelViewControllerWithCoroutinesTest : advanceUntilIdle() } + @Test + fun doubleTapRequired_shadeLocked_oneWayHapticsDisabled_usesOldVibrate() = runTest { + launch(Dispatchers.Main.immediate) { + whenever(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(false) + val listener = getFalsingTapListener() + val packageName = mView.context.packageName + mStatusBarStateController.setState(SHADE_LOCKED) + + listener.onAdditionalTapRequired() + verify(mVibratorHelper) + .vibrate( + any(), + eq(packageName), + eq(ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT), + eq("falsing-additional-tap-required"), + eq(VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES) + ) + + verify(mTapAgainViewController).show() + } + advanceUntilIdle() + } + + @Test + fun doubleTapRequired_shadeLocked_oneWayHapticsEnabled_usesPerformHapticFeedback() = runTest { + launch(Dispatchers.Main.immediate) { + whenever(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true) + val listener = getFalsingTapListener() + mStatusBarStateController.setState(SHADE_LOCKED) + + listener.onAdditionalTapRequired() + verify(mVibratorHelper) + .performHapticFeedback(eq(mView), eq(HapticFeedbackConstants.REJECT)) + + verify(mTapAgainViewController).show() + } + advanceUntilIdle() + } + @Test fun testOnAttachRefreshStatusBarState() = runTest { launch(Dispatchers.Main.immediate) {