diff options
9 files changed, 319 insertions, 13 deletions
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index c16f13ef5ae6..09d46856dec8 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -129,7 +129,21 @@ android:layout_marginTop="@dimen/status_bar_header_height_keyguard" android:text="@string/report_rejected_touch" android:visibility="gone" /> - + <com.android.systemui.statusbar.phone.TapAgainView + android:id="@+id/shade_falsing_tap_again" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + systemui:layout_constraintLeft_toLeftOf="parent" + systemui:layout_constraintRight_toRightOf="parent" + systemui:layout_constraintBottom_toBottomOf="parent" + android:layout_marginBottom="20dp" + android:paddingLeft="20dp" + android:paddingRight="20dp" + android:paddingTop="10dp" + android:paddingBottom="10dp" + android:elevation="10dp" + android:visibility="gone" + /> </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer> <FrameLayout diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java index ee69e277cc46..dba530edc27f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -40,6 +40,7 @@ public class FalsingManagerFake implements FalsingManager { private boolean mIsFalseRobustTap; private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>(); + private final List<FalsingTapListener> mTapListeners = new ArrayList<>(); @Override public void onSuccessfulUnlock() { @@ -148,11 +149,15 @@ public class FalsingManagerFake implements FalsingManager { @Override public void addTapListener(FalsingTapListener falsingTapListener) { - + mTapListeners.add(falsingTapListener); } @Override public void removeTapListener(FalsingTapListener falsingTapListener) { + mTapListeners.remove(falsingTapListener); + } + public List<FalsingTapListener> getTapListeners() { + return mTapListeners; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java index d84bb908fe69..68e20705fbeb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java @@ -142,7 +142,6 @@ public class KeyguardIndicationTextView extends TextView { Animator yTranslate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0, -getYTranslationPixels()); yTranslate.setDuration(getFadeOutDuration()); - fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); animatorSet.playTogether(fadeOut, yTranslate); return animatorSet; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 0f3af095f7be..d9ba494a4d63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -24,6 +24,8 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; +import com.android.systemui.R; + public class NotificationPanelView extends PanelView { private static final boolean DEBUG = false; @@ -92,6 +94,10 @@ public class NotificationPanelView extends PanelView { mRtlChangeListener = listener; } + public TapAgainView getTapAgainView() { + return findViewById(R.id.shade_falsing_tap_again); + } + interface RtlChangeListener { void onRtlPropertielsChanged(int layoutDirection); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 1263b104b6a5..def9092e4171 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -97,8 +97,8 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeLog; -import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; +import com.android.systemui.fragments.FragmentService; import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; @@ -306,6 +306,7 @@ public class NotificationPanelViewController extends PanelViewController { private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory; private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; private final QSDetailDisplayer mQSDetailDisplayer; + private final FragmentService mFragmentService; private final FeatureFlags mFeatureFlags; private final ScrimController mScrimController; private final PrivacyDotViewController mPrivacyDotViewController; @@ -314,6 +315,7 @@ public class NotificationPanelViewController extends PanelViewController { // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications private final int mMaxKeyguardNotifications; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; + private final TapAgainViewController mTapAgainViewController; private boolean mShouldUseSplitNotificationShade; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; @@ -604,7 +606,12 @@ public class NotificationPanelViewController extends PanelViewController { private final FalsingTapListener mFalsingTapListener = new FalsingTapListener() { @Override public void onDoubleTapRequired() { - showTransientIndication(R.string.notification_tap_again); + if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) { + mTapAgainViewController.show(); + } else { + mKeyguardIndicationController.showTransientIndication( + R.string.notification_tap_again); + } mVibratorHelper.vibrate(VibrationEffect.EFFECT_STRENGTH_MEDIUM); } }; @@ -653,6 +660,8 @@ public class NotificationPanelViewController extends PanelViewController { QuickAccessWalletClient quickAccessWalletClient, KeyguardMediaController keyguardMediaController, PrivacyDotViewController privacyDotViewController, + TapAgainViewController tapAgainViewController, + FragmentService fragmentService, @Main Executor uiExecutor, SecureSettings secureSettings) { super(view, falsingManager, dozeLog, keyguardStateController, @@ -679,6 +688,7 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory; mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory; mQSDetailDisplayer = qsDetailDisplayer; + mFragmentService = fragmentService; mKeyguardUserSwitcherEnabled = mResources.getBoolean( com.android.internal.R.bool.config_keyguardUserSwitcher); mKeyguardQsUserSwitchEnabled = @@ -705,6 +715,7 @@ public class NotificationPanelViewController extends PanelViewController { mUserManager = userManager; mMediaDataManager = mediaDataManager; mQuickAccessWalletClient = quickAccessWalletClient; + mTapAgainViewController = tapAgainViewController; mUiExecutor = uiExecutor; mSecureSettings = secureSettings; pulseExpansionHandler.setPulseExpandAbortListener(() -> { @@ -843,6 +854,8 @@ public class NotificationPanelViewController extends PanelViewController { if (mShouldUseSplitNotificationShade) { updateResources(); } + + mTapAgainViewController.init(); } @Override @@ -3675,10 +3688,6 @@ public class NotificationPanelViewController extends PanelViewController { updateMaxDisplayedNotifications(true); } - public void showTransientIndication(int id) { - mKeyguardIndicationController.showTransientIndication(id); - } - public void setAlpha(float alpha) { mView.setAlpha(alpha); } @@ -4264,7 +4273,8 @@ public class NotificationPanelViewController extends PanelViewController { private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener { @Override public void onViewAttachedToWindow(View v) { - FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener); + mFragmentService.getFragmentHostManager(mView) + .addTagListener(QS.TAG, mFragmentListener); mStatusBarStateController.addCallback(mStatusBarStateListener); mConfigurationController.addCallback(mConfigurationListener); mUpdateMonitor.registerCallback(mKeyguardUpdateCallback); @@ -4278,7 +4288,8 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onViewDetachedFromWindow(View v) { - FragmentHostManager.get(mView).removeTagListener(QS.TAG, mFragmentListener); + mFragmentService.getFragmentHostManager(mView) + .removeTagListener(QS.TAG, mFragmentListener); mStatusBarStateController.removeCallback(mStatusBarStateListener); mConfigurationController.removeCallback(mConfigurationListener); mUpdateMonitor.removeCallback(mKeyguardUpdateCallback); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java new file mode 100644 index 000000000000..9856795c8903 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.systemui.R; +import com.android.wm.shell.animation.Interpolators; + +/** + * View to show a toast-like popup on the notification shade and quick settings. + */ +public class TapAgainView extends FrameLayout { + public TapAgainView( + @NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + updateBgColor(); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + TextView text = new TextView(mContext); + text.setText(R.string.notification_tap_again); + addView(text); + } + + void updateBgColor() { + setBackgroundResource(R.drawable.rounded_bg_full); + } + + /** Make the view visible. */ + public void animateIn() { + int yTranslation = mContext.getResources().getDimensionPixelSize( + R.dimen.keyguard_indication_y_translation); + + AnimatorSet animatorSet = new AnimatorSet(); + ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f); + fadeIn.setStartDelay(150); // From KeyguardIndicationTextView#getFadeInDelay + fadeIn.setDuration(317); // From KeyguardIndicationTextView#getFadeInDuration + fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + + Animator yTranslate = + ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, yTranslation, 0); + yTranslate.setDuration(600); // From KeyguardIndicationTextView#getYInDuration + yTranslate.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + setTranslationY(0); + } + }); + animatorSet.playTogether(yTranslate, fadeIn); + animatorSet.start(); + setVisibility(View.VISIBLE); + } + + /** Make the view gone. */ + public void animateOut() { + long fadeOutDuration = 167L; // From KeyguardIndicationTextView#getFadeOutDuration + int yTranslation = mContext.getResources().getDimensionPixelSize( + com.android.systemui.R.dimen.keyguard_indication_y_translation); + + AnimatorSet animatorSet = new AnimatorSet(); + ObjectAnimator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f); + fadeOut.setDuration(fadeOutDuration); + fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + + Animator yTranslate = + ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0, -yTranslation); + yTranslate.setDuration(fadeOutDuration); + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setVisibility(GONE); + } + + @Override + public void onAnimationCancel(Animator animation) { + setVisibility(GONE); + } + }); + animatorSet.playTogether(yTranslate, fadeOut); + animatorSet.start(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java new file mode 100644 index 000000000000..bb53bad7df70 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TIMEOUT_MS; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.util.ViewController; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * Controller for {@link TapAgainView}. + */ +@StatusBarComponent.StatusBarScope +public class TapAgainViewController extends ViewController<TapAgainView> { + private final DelayableExecutor mDelayableExecutor; + private final ConfigurationController mConfigurationController; + private final long mDoubleTapTimeMs; + + private Runnable mHideCanceler; + + @VisibleForTesting + final ConfigurationListener mConfigurationListener = new ConfigurationListener() { + @Override + public void onOverlayChanged() { + mView.updateBgColor(); + } + + @Override + public void onUiModeChanged() { + mView.updateBgColor(); + } + + @Override + public void onThemeChanged() { + mView.updateBgColor(); + } + }; + + @Inject + protected TapAgainViewController(TapAgainView view, + @Main DelayableExecutor delayableExecutor, + ConfigurationController configurationController, + @Named(DOUBLE_TAP_TIMEOUT_MS) long doubleTapTimeMs) { + super(view); + mDelayableExecutor = delayableExecutor; + mConfigurationController = configurationController; + mDoubleTapTimeMs = doubleTapTimeMs; + } + + @Override + protected void onViewAttached() { + mConfigurationController.addCallback(mConfigurationListener); + } + + @Override + protected void onViewDetached() { + mConfigurationController.removeCallback(mConfigurationListener); + } + + /** Shows the associated view, possibly animating it. */ + public void show() { + if (mHideCanceler != null) { + mHideCanceler.run(); + } + mView.animateIn(); + mHideCanceler = mDelayableExecutor.executeDelayed(this::hide, mDoubleTapTimeMs); + } + + /** Hides the associated view, possibly animating it. */ + public void hide() { + mHideCanceler = null; + mView.animateOut(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index 008c0aea7ce9..27d71edd5e8a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -23,6 +23,7 @@ import com.android.systemui.R; import com.android.systemui.biometrics.AuthRippleView; import com.android.systemui.statusbar.phone.NotificationPanelView; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; +import com.android.systemui.statusbar.phone.TapAgainView; import dagger.Module; import dagger.Provides; @@ -53,4 +54,11 @@ public abstract class StatusBarViewModule { NotificationShadeWindowView notificationShadeWindowView) { return notificationShadeWindowView.findViewById(R.id.auth_ripple); } + + /** */ + @Provides + @StatusBarComponent.StatusBarScope + public static TapAgainView getTapAgainView(NotificationPanelView npv) { + return npv.getTapAgainView(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 6b4797fc5723..83a1872a0a45 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -20,12 +20,15 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; +import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; +import static com.android.systemui.statusbar.notification.ViewGroupFadeHelper.reset; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -75,13 +78,17 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.doze.DozeLog; +import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.fragments.FragmentService; import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.QSDetailDisplayer; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.KeyguardAffordanceView; +import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -252,11 +259,21 @@ public class NotificationPanelViewTest extends SysuiTestCase { private PrivacyDotViewController mPrivacyDotViewController; @Mock private SecureSettings mSecureSettings; + @Mock + private TapAgainViewController mTapAgainViewController; + @Mock + private KeyguardIndicationController mKeyguardIndicationController; + @Mock + private FragmentService mFragmentService; + @Mock + private FragmentHostManager mFragmentHostManager; private SysuiStatusBarStateController mStatusBarStateController; private NotificationPanelViewController mNotificationPanelViewController; private View.AccessibilityDelegate mAccessibiltyDelegate; private NotificationsQuickSettingsContainer mNotificationContainerParent; + private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners; + private FalsingManagerFake mFalsingManager = new FalsingManagerFake(); @Before public void setup() { @@ -297,6 +314,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mNotificationContainerParent.addView(newViewWithId(R.id.keyguard_status_view)); when(mView.findViewById(R.id.notification_container_parent)) .thenReturn(mNotificationContainerParent); + when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager); FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( mDisplayMetrics); @@ -317,7 +335,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mKeyguardBypassController, mHeadsUpManager, mock(NotificationRoundnessManager.class), mStatusBarStateController, - new FalsingManagerFake(), + mFalsingManager, mLockscreenShadeTransitionController, new FalsingCollectorFake()); when(mKeyguardStatusViewComponentFactory.build(any())) @@ -331,11 +349,12 @@ public class NotificationPanelViewTest extends SysuiTestCase { when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController()) .thenReturn(mKeyguardStatusBarViewController); + reset(mView); mNotificationPanelViewController = new NotificationPanelViewController(mView, mResources, mLayoutInflater, coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, - new FalsingManagerFake(), new FalsingCollectorFake(), + mFalsingManager, new FalsingCollectorFake(), mNotificationLockscreenUserManager, mNotificationEntryManager, mKeyguardStateController, mStatusBarStateController, mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper, @@ -364,6 +383,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { mQuickAccessWalletClient, mKeyguardMediaController, mPrivacyDotViewController, + mTapAgainViewController, + mFragmentService, new FakeExecutor(new FakeSystemClock()), mSecureSettings); mNotificationPanelViewController.initDependencies( @@ -371,6 +392,13 @@ public class NotificationPanelViewTest extends SysuiTestCase { mNotificationShelfController); mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); mNotificationPanelViewController.setBar(mPanelBar); + mNotificationPanelViewController.setKeyguardIndicationController( + mKeyguardIndicationController); + ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + verify(mView, atLeast(1)).addOnAttachStateChangeListener( + onAttachStateChangeListenerArgumentCaptor.capture()); + mOnAttachStateChangeListeners = onAttachStateChangeListenerArgumentCaptor.getAllValues(); ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor = ArgumentCaptor.forClass(View.AccessibilityDelegate.class); @@ -616,6 +644,34 @@ public class NotificationPanelViewTest extends SysuiTestCase { verify(mKeyguardStateController).notifyPanelFlingEnd(); } + @Test + public void testDoubleTapRequired_Keyguard() { + FalsingManager.FalsingTapListener listener = getFalsingTapListener(); + mStatusBarStateController.setState(KEYGUARD); + + listener.onDoubleTapRequired(); + + verify(mKeyguardIndicationController).showTransientIndication(anyInt()); + } + + @Test + public void testDoubleTapRequired_ShadeLocked() { + FalsingManager.FalsingTapListener listener = getFalsingTapListener(); + mStatusBarStateController.setState(SHADE_LOCKED); + + listener.onDoubleTapRequired(); + + verify(mTapAgainViewController).show(); + } + + private FalsingManager.FalsingTapListener getFalsingTapListener() { + for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { + listener.onViewAttachedToWindow(mView); + } + assertThat(mFalsingManager.getTapListeners().size()).isEqualTo(1); + return mFalsingManager.getTapListeners().get(0); + } + private View newViewWithId(int id) { View view = new View(mContext); view.setId(id); |