Enabled dragging down from the lock screen when bypassing

The pulseExpansionHandler now also works on the lockscreen.
This also delays the bypass when the user is currently
dragging down.

Bug: 134094877
Bug: 130327302
Test: atest SystemUITests
Change-Id: I8d5b5b53e9a68e08933866df6831ecbada41ce43
This commit is contained in:
Selim Cinek
2019-06-14 16:37:53 -07:00
parent b57dd8ab17
commit b8cc6efc0a
8 changed files with 151 additions and 52 deletions

View File

@@ -25,6 +25,7 @@ import android.os.PowerManager
import android.os.PowerManager.WAKE_REASON_GESTURE
import android.os.SystemClock
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.ViewConfiguration
import com.android.systemui.Gefingerpoken
@@ -36,6 +37,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.ShadeController
import javax.inject.Inject
@@ -48,22 +50,31 @@ import kotlin.math.max
@Singleton
class PulseExpansionHandler @Inject
constructor(context: Context,
private val mWakeUpCoordinator: NotificationWakeUpCoordinator) : Gefingerpoken {
private val wakeUpCoordinator: NotificationWakeUpCoordinator,
private val bypassController: KeyguardBypassController) : Gefingerpoken {
companion object {
private val RUBBERBAND_FACTOR_STATIC = 0.25f
private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
}
private val mPowerManager: PowerManager?
private var mShadeController: ShadeController? = null
private lateinit var shadeController: ShadeController
private val mMinDragDistance: Int
private var mInitialTouchX: Float = 0.0f
private var mInitialTouchY: Float = 0.0f
var isExpanding: Boolean = false
private set
private var isExpanding: Boolean = false
private set(value) {
val changed = field != value
field = value
bypassController.isPulseExpanding = value
if (changed && !value && !leavingLockscreen) {
bypassController.maybePerformPendingUnlock()
}
}
private var leavingLockscreen: Boolean = false
private val mTouchSlop: Float
private var mExpansionCallback: ExpansionCallback? = null
private lateinit var mStackScroller: NotificationStackScrollLayout
private lateinit var expansionCallback: ExpansionCallback
private lateinit var stackScroller: NotificationStackScrollLayout
private val mTemp2 = IntArray(2)
private var mDraggedFarEnough: Boolean = false
private var mStartingChild: ExpandableView? = null
@@ -74,6 +85,7 @@ constructor(context: Context,
private var mEmptyDragAmount: Float = 0.0f
private var mWakeUpHeight: Float = 0.0f
private var mReachedWakeUpHeight: Boolean = false
private var velocityTracker: VelocityTracker? = null
private val isFalseTouch: Boolean
get() = mFalsingManager.isFalseTouch
@@ -91,9 +103,13 @@ constructor(context: Context,
}
private fun maybeStartExpansion(event: MotionEvent): Boolean {
if (!mPulsing) {
if (!wakeUpCoordinator.canShowPulsingHuns) {
return false
}
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain()
}
velocityTracker!!.addMovement(event)
val x = event.x
val y = event.y
@@ -101,6 +117,7 @@ constructor(context: Context,
MotionEvent.ACTION_DOWN -> {
mDraggedFarEnough = false
isExpanding = false
leavingLockscreen = false
mStartingChild = null
mInitialTouchY = y
mInitialTouchX = x
@@ -114,29 +131,52 @@ constructor(context: Context,
captureStartingChild(mInitialTouchX, mInitialTouchY)
mInitialTouchY = y
mInitialTouchX = x
mWakeUpHeight = mWakeUpCoordinator.getWakeUpHeight()
mWakeUpHeight = wakeUpCoordinator.getWakeUpHeight()
mReachedWakeUpHeight = false
return true
}
}
MotionEvent.ACTION_UP -> {
recycleVelocityTracker();
}
MotionEvent.ACTION_CANCEL -> {
recycleVelocityTracker();
}
}
return false
}
private fun recycleVelocityTracker() {
velocityTracker?.recycle();
velocityTracker = null
}
override fun onTouchEvent(event: MotionEvent): Boolean {
if (!isExpanding) {
return maybeStartExpansion(event)
}
velocityTracker!!.addMovement(event)
val y = event.y
val moveDistance = y - mInitialTouchY
when (event.actionMasked) {
MotionEvent.ACTION_MOVE -> updateExpansionHeight(y - mInitialTouchY)
MotionEvent.ACTION_UP -> if (!mFalsingManager.isUnlockingDisabled && !isFalseTouch) {
finishExpansion()
} else {
cancelExpansion()
MotionEvent.ACTION_MOVE -> updateExpansionHeight(moveDistance)
MotionEvent.ACTION_UP -> {
velocityTracker!!.computeCurrentVelocity(1000 /* units */)
val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000
if (!mFalsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) {
finishExpansion()
} else {
cancelExpansion()
}
recycleVelocityTracker()
}
MotionEvent.ACTION_CANCEL -> {
cancelExpansion()
recycleVelocityTracker()
}
MotionEvent.ACTION_CANCEL -> cancelExpansion()
}
return isExpanding
}
@@ -147,12 +187,15 @@ constructor(context: Context,
setUserLocked(mStartingChild!!, false)
mStartingChild = null
}
if (shadeController.isDozing) {
isWakingToShadeLocked = true
wakeUpCoordinator.willWakeUp = true
mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), WAKE_REASON_GESTURE,
"com.android.systemui:PULSEDRAG")
}
shadeController.goToLockedShade(mStartingChild)
leavingLockscreen = true;
isExpanding = false
isWakingToShadeLocked = true
mWakeUpCoordinator.willWakeUp = true
mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), WAKE_REASON_GESTURE,
"com.android.systemui:PULSEDRAG")
mShadeController!!.goToLockedShade(mStartingChild)
if (mStartingChild is ExpandableNotificationRow) {
val row = mStartingChild as ExpandableNotificationRow?
row!!.onExpandedByGesture(true /* userExpanded */)
@@ -172,12 +215,12 @@ constructor(context: Context,
expansionHeight = max(newHeight.toFloat(), expansionHeight)
} else {
val target = if (mReachedWakeUpHeight) mWakeUpHeight else 0.0f
mWakeUpCoordinator.setNotificationsVisibleForExpansion(height > target,
wakeUpCoordinator.setNotificationsVisibleForExpansion(height > target,
true /* animate */,
true /* increaseSpeed */)
expansionHeight = max(mWakeUpHeight, expansionHeight)
}
val emptyDragAmount = mWakeUpCoordinator.setPulseHeight(expansionHeight)
val emptyDragAmount = wakeUpCoordinator.setPulseHeight(expansionHeight)
setEmptyDragAmount(emptyDragAmount * RUBBERBAND_FACTOR_STATIC)
}
@@ -192,7 +235,7 @@ constructor(context: Context,
private fun setEmptyDragAmount(amount: Float) {
mEmptyDragAmount = amount
mExpansionCallback!!.setEmptyDragAmount(amount)
expansionCallback.setEmptyDragAmount(amount)
}
private fun reset(child: ExpandableView) {
@@ -234,7 +277,7 @@ constructor(context: Context,
} else {
resetClock()
}
mWakeUpCoordinator.setNotificationsVisibleForExpansion(false /* visible */,
wakeUpCoordinator.setNotificationsVisibleForExpansion(false /* visible */,
true /* animate */,
false /* increaseSpeed */)
isExpanding = false
@@ -243,21 +286,21 @@ constructor(context: Context,
private fun findView(x: Float, y: Float): ExpandableView? {
var totalX = x
var totalY = y
mStackScroller.getLocationOnScreen(mTemp2)
stackScroller.getLocationOnScreen(mTemp2)
totalX += mTemp2[0].toFloat()
totalY += mTemp2[1].toFloat()
val childAtRawPosition = mStackScroller.getChildAtRawPosition(totalX, totalY)
val childAtRawPosition = stackScroller.getChildAtRawPosition(totalX, totalY)
return if (childAtRawPosition != null && childAtRawPosition.isContentExpandable) {
childAtRawPosition
} else null
}
fun setUp(notificationStackScroller: NotificationStackScrollLayout,
expansionCallback: ExpansionCallback,
shadeController: ShadeController) {
mExpansionCallback = expansionCallback
mShadeController = shadeController
mStackScroller = notificationStackScroller
fun setUp(stackScroller: NotificationStackScrollLayout,
expansionCallback: ExpansionCallback,
shadeController: ShadeController) {
this.expansionCallback = expansionCallback
this.shadeController = shadeController
this.stackScroller = stackScroller
}
fun setPulsing(pulsing: Boolean) {

View File

@@ -89,6 +89,21 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
}
/**
* True if we can show pulsing heads up notifications
*/
var canShowPulsingHuns: Boolean = false
private set
get() {
var canShow = pulsing
if (bypassController.bypassEnabled) {
// We also allow pulsing on the lock screen!
canShow = canShow || (mWakingUp || willWakeUp || fullyAwake)
&& mStatusBarStateController.state == StatusBarState.KEYGUARD
}
return canShow
}
init {
mHeadsUpManagerPhone.addListener(this)
@@ -120,13 +135,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) {
// TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore
var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications()
var canShow = pulsing
if (bypassController.bypassEnabled) {
// We also allow pulsing on the lock screen!
canShow = canShow || (mWakingUp || willWakeUp || fullyAwake)
&& mStatusBarStateController.state == StatusBarState.KEYGUARD
}
visible = visible && canShow
visible = visible && canShowPulsingHuns
if (!visible && mNotificationsVisible && (mWakingUp || willWakeUp) && mDozeAmount != 0.0f) {
// let's not make notifications invisible while waking up, otherwise the animation

View File

@@ -242,7 +242,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
}
}
private void startWakeAndUnlock(BiometricSourceType biometricSourceType) {
public void startWakeAndUnlock(BiometricSourceType biometricSourceType) {
startWakeAndUnlock(calculateMode(biometricSourceType));
}

View File

@@ -33,20 +33,32 @@ class KeyguardBypassController {
private val unlockMethodCache: UnlockMethodCache
private val statusBarStateController: StatusBarStateController
/**
* If face unlock dismisses the lock screen or keeps user on keyguard for the current user.
*/
lateinit var unlockController: BiometricUnlockController
var isPulseExpanding = false
/**
* If face unlock dismisses the lock screen or keeps user on keyguard for the current user.
*/
var bypassEnabled: Boolean = false
get() = field && unlockMethodCache.isUnlockingWithFacePossible
private set
lateinit var unlockController: BiometricUnlockController
/**
* The pending unlock type which is set if the bypass was blocked when it happened.
*/
private var pendingUnlockType: BiometricSourceType? = null
@Inject
constructor(context: Context, tunerService: TunerService,
statusBarStateController: StatusBarStateController) {
unlockMethodCache = UnlockMethodCache.getInstance(context)
this.statusBarStateController = statusBarStateController
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
override fun onStateChanged(newState: Int) {
if (newState != StatusBarState.KEYGUARD) {
pendingUnlockType = null;
}
}
})
val faceManager = context.getSystemService(FaceManager::class.java)
if (faceManager?.isHardwareDetected != true) {
return
@@ -72,11 +84,30 @@ class KeyguardBypassController {
* @return false if we can not wake and unlock right now
*/
fun onBiometricAuthenticated(biometricSourceType: BiometricSourceType): Boolean {
if (bypassEnabled && statusBarStateController.state != StatusBarState.KEYGUARD) {
// We're bypassing but not actually on the lockscreen, the user should decide when
// to unlock
return false
if (bypassEnabled) {
if (statusBarStateController.state != StatusBarState.KEYGUARD) {
// We're bypassing but not actually on the lockscreen, the user should decide when
// to unlock
return false
}
if (isPulseExpanding) {
pendingUnlockType = biometricSourceType
return false
}
}
return true
}
fun maybePerformPendingUnlock() {
if (pendingUnlockType != null) {
if (onBiometricAuthenticated(pendingUnlockType!!)) {
unlockController.startWakeAndUnlock(pendingUnlockType)
pendingUnlockType = null
}
}
}
fun onStartedGoingToSleep() {
pendingUnlockType = null
}
}

View File

@@ -213,6 +213,8 @@ public class NotificationPanelView extends PanelView implements
private int mNotificationsHeaderCollideDistance;
private int mUnlockMoveDistance;
private float mEmptyDragAmount;
private float mDownX;
private float mDownY;
private final KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
new KeyguardClockPositionAlgorithm();
@@ -902,7 +904,8 @@ public class NotificationPanelView extends PanelView implements
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
return true;
}
if (mPulseExpansionHandler.onInterceptTouchEvent(event)) {
if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0)
&& mPulseExpansionHandler.onInterceptTouchEvent(event)) {
return true;
}
@@ -1003,6 +1006,8 @@ public class NotificationPanelView extends PanelView implements
mOnlyAffordanceInThisMotion = false;
mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
mDozingOnDown = isDozing();
mDownX = event.getX();
mDownY = event.getY();
mCollapsedOnDown = isFullyCollapsed();
mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
}
@@ -1076,7 +1081,8 @@ public class NotificationPanelView extends PanelView implements
|| event.getAction() == MotionEvent.ACTION_CANCEL) {
mBlockingExpansionForCurrentTouch = false;
}
if (!mIsExpanding && mPulseExpansionHandler.onTouchEvent(event)) {
if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0)
&& mPulseExpansionHandler.onTouchEvent(event)) {
// We're expanding all the other ones shouldn't get this anymore
return true;
}

View File

@@ -786,6 +786,7 @@ public class StatusBar extends SystemUI implements DemoMode,
inflateStatusBarWindow(context);
mStatusBarWindow.setService(this);
mStatusBarWindow.setBypassController(mKeyguardBypassController);
mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
// TODO: Deal with the ugliness that comes from having some of the statusbar broken out
@@ -3622,6 +3623,7 @@ public class StatusBar extends SystemUI implements DemoMode,
notifyHeadsUpGoingToSleep();
dismissVolumeDialog();
mWakeUpCoordinator.setFullyAwake(false);
mKeyguardBypassController.onStartedGoingToSleep();
}
@Override

View File

@@ -149,6 +149,7 @@ public class StatusBarWindowView extends FrameLayout {
* events manually as it's outside of the regular view bounds.
*/
private boolean mExpandingBelowNotch;
private KeyguardBypassController mBypassController;
public StatusBarWindowView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -417,6 +418,7 @@ public class StatusBarWindowView extends FrameLayout {
if (mNotificationPanel.isFullyExpanded()
&& mStatusBarStateController.getState() == StatusBarState.KEYGUARD
&& !mService.isBouncerShowing()
&& !mBypassController.getBypassEnabled()
&& !mService.isDozing()) {
intercept = mDragDownHelper.onInterceptTouchEvent(ev);
}
@@ -439,7 +441,8 @@ public class StatusBarWindowView extends FrameLayout {
if (mService.isDozing()) {
handled = !mService.isPulsing();
}
if ((mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !handled)
if ((mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !handled
&& !mBypassController.getBypassEnabled())
|| mDragDownHelper.isDraggingDown()) {
// we still want to finish our drag down gesture when locking the screen
handled = mDragDownHelper.onTouchEvent(ev);
@@ -518,6 +521,10 @@ public class StatusBarWindowView extends FrameLayout {
}
}
public void setBypassController(KeyguardBypassController bypassController) {
mBypassController = bypassController;
}
public class LayoutParams extends FrameLayout.LayoutParams {
public boolean ignoreRightInset;

View File

@@ -118,7 +118,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mock(HeadsUpManagerPhone.class),
new StatusBarStateControllerImpl(),
bypassController);
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator);
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator,
bypassController);
mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler,
bypassController);
mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);