diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java deleted file mode 100644 index 037812312aa72..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2014 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; - -import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; - -import com.android.systemui.ExpandHelper; -import com.android.systemui.Gefingerpoken; -import com.android.systemui.R; -import com.android.systemui.animation.Interpolators; -import com.android.systemui.classifier.FalsingCollector; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.statusbar.notification.row.ExpandableView; - -/** - * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand - * the notification where the drag started. - */ -public class DragDownHelper implements Gefingerpoken { - - private static final float RUBBERBAND_FACTOR_EXPANDABLE = 0.5f; - private static final float RUBBERBAND_FACTOR_STATIC = 0.15f; - - private static final int SPRING_BACK_ANIMATION_LENGTH_MS = 375; - - private int mMinDragDistance; - private final FalsingManager mFalsingManager; - private ExpandHelper.Callback mCallback; - private float mInitialTouchX; - private float mInitialTouchY; - private boolean mDraggingDown; - private final float mTouchSlop; - private final float mSlopMultiplier; - private DragDownCallback mDragDownCallback; - private View mHost; - private final int[] mTemp2 = new int[2]; - private boolean mDraggedFarEnough; - private ExpandableView mStartingChild; - private float mLastHeight; - private FalsingCollector mFalsingCollector; - - public DragDownHelper(Context context, View host, ExpandHelper.Callback callback, - DragDownCallback dragDownCallback, FalsingManager falsingManager, - FalsingCollector falsingCollector) { - mMinDragDistance = context.getResources().getDimensionPixelSize( - R.dimen.keyguard_drag_down_min_distance); - mFalsingManager = falsingManager; - final ViewConfiguration configuration = ViewConfiguration.get(context); - mTouchSlop = configuration.getScaledTouchSlop(); - mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier(); - mCallback = callback; - mDragDownCallback = dragDownCallback; - mHost = host; - mFalsingCollector = falsingCollector; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - final float x = event.getX(); - final float y = event.getY(); - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - mDraggedFarEnough = false; - mDraggingDown = false; - mStartingChild = null; - mInitialTouchY = y; - mInitialTouchX = x; - break; - - case MotionEvent.ACTION_MOVE: - final float h = y - mInitialTouchY; - // Adjust the touch slop if another gesture may be being performed. - final float touchSlop = - event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE - ? mTouchSlop * mSlopMultiplier - : mTouchSlop; - if (h > touchSlop && h > Math.abs(x - mInitialTouchX)) { - mFalsingCollector.onNotificationStartDraggingDown(); - mDraggingDown = true; - captureStartingChild(mInitialTouchX, mInitialTouchY); - mInitialTouchY = y; - mInitialTouchX = x; - mDragDownCallback.onTouchSlopExceeded(); - return mStartingChild != null || mDragDownCallback.isDragDownAnywhereEnabled(); - } - break; - } - return false; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (!mDraggingDown) { - return false; - } - final float x = event.getX(); - final float y = event.getY(); - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_MOVE: - mLastHeight = y - mInitialTouchY; - captureStartingChild(mInitialTouchX, mInitialTouchY); - if (mStartingChild != null) { - handleExpansion(mLastHeight, mStartingChild); - } else { - mDragDownCallback.setEmptyDragAmount(mLastHeight); - } - if (mLastHeight > mMinDragDistance) { - if (!mDraggedFarEnough) { - mDraggedFarEnough = true; - mDragDownCallback.onCrossedThreshold(true); - } - } else { - if (mDraggedFarEnough) { - mDraggedFarEnough = false; - mDragDownCallback.onCrossedThreshold(false); - } - } - return true; - case MotionEvent.ACTION_UP: - if (!mFalsingManager.isUnlockingDisabled() && mDragDownCallback.canDragDown() - && !isFalseTouch()) { - mDragDownCallback.onDraggedDown(mStartingChild, (int) (y - mInitialTouchY)); - if (mStartingChild == null) { - cancelExpansion(); - } else { - mCallback.setUserLockedChild(mStartingChild, false); - mStartingChild = null; - } - mDraggingDown = false; - } else { - stopDragging(); - return false; - } - break; - case MotionEvent.ACTION_CANCEL: - stopDragging(); - return false; - } - return false; - } - - private boolean isFalseTouch() { - if (!mDragDownCallback.isFalsingCheckNeeded()) { - return false; - } - return mFalsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) || !mDraggedFarEnough; - } - - private void captureStartingChild(float x, float y) { - if (mStartingChild == null) { - mStartingChild = findView(x, y); - if (mStartingChild != null) { - if (mDragDownCallback.isDragDownEnabledForView(mStartingChild)) { - mCallback.setUserLockedChild(mStartingChild, true); - } else { - mStartingChild = null; - } - } - } - } - - private void handleExpansion(float heightDelta, ExpandableView child) { - if (heightDelta < 0) { - heightDelta = 0; - } - boolean expandable = child.isContentExpandable(); - float rubberbandFactor = expandable - ? RUBBERBAND_FACTOR_EXPANDABLE - : RUBBERBAND_FACTOR_STATIC; - float rubberband = heightDelta * rubberbandFactor; - if (expandable - && (rubberband + child.getCollapsedHeight()) > child.getMaxContentHeight()) { - float overshoot = - (rubberband + child.getCollapsedHeight()) - child.getMaxContentHeight(); - overshoot *= (1 - RUBBERBAND_FACTOR_STATIC); - rubberband -= overshoot; - } - child.setActualHeight((int) (child.getCollapsedHeight() + rubberband)); - } - - private void cancelExpansion(final ExpandableView child) { - if (child.getActualHeight() == child.getCollapsedHeight()) { - mCallback.setUserLockedChild(child, false); - return; - } - ObjectAnimator anim = ObjectAnimator.ofInt(child, "actualHeight", - child.getActualHeight(), child.getCollapsedHeight()); - anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mCallback.setUserLockedChild(child, false); - } - }); - anim.start(); - } - - private void cancelExpansion() { - ValueAnimator anim = ValueAnimator.ofFloat(mLastHeight, 0); - anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS); - anim.addUpdateListener(animation -> { - mDragDownCallback.setEmptyDragAmount((Float) animation.getAnimatedValue()); - }); - anim.start(); - } - - private void stopDragging() { - mFalsingCollector.onNotificationStopDraggingDown(); - if (mStartingChild != null) { - cancelExpansion(mStartingChild); - mStartingChild = null; - } else { - cancelExpansion(); - } - mDraggingDown = false; - mDragDownCallback.onDragDownReset(); - } - - private ExpandableView findView(float x, float y) { - mHost.getLocationOnScreen(mTemp2); - x += mTemp2[0]; - y += mTemp2[1]; - return mCallback.getChildAtRawPosition(x, y); - } - - public boolean isDraggingDown() { - return mDraggingDown; - } - - public boolean isDragDownEnabled() { - return mDragDownCallback.isDragDownEnabledForView(null); - } - - public interface DragDownCallback { - - /** - * @return true if the interaction is accepted, false if it should be cancelled - */ - boolean canDragDown(); - - /** Call when a view has been dragged. */ - void onDraggedDown(View startingChild, int dragLengthY); - void onDragDownReset(); - - /** - * The user has dragged either above or below the threshold - * @param above whether he dragged above it - */ - void onCrossedThreshold(boolean above); - void onTouchSlopExceeded(); - void setEmptyDragAmount(float amount); - boolean isFalsingCheckNeeded(); - - /** - * Is dragging down enabled on a given view - * @param view The view to check or {@code null} to check if it's enabled at all - */ - boolean isDragDownEnabledForView(ExpandableView view); - - /** - * @return if drag down is enabled anywhere, not just on selected views. - */ - boolean isDragDownAnywhereEnabled(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt new file mode 100644 index 0000000000000..6d91ccba01560 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -0,0 +1,263 @@ +package com.android.systemui.statusbar + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ObjectAnimator +import android.animation.ValueAnimator +import android.content.Context +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration +import com.android.systemui.ExpandHelper +import com.android.systemui.Gefingerpoken +import com.android.systemui.animation.Interpolators +import com.android.systemui.R +import com.android.systemui.classifier.Classifier +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.notification.row.ExpandableView + +/** + * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand + * the notification where the drag started. + */ +class DragDownHelper(private val falsingManager: FalsingManager, + private val expandCallback: ExpandHelper.Callback, + private val dragDownCallback: DragDownCallback, + private val falsingCollector: FalsingCollector, + private val host: View, + context: Context): Gefingerpoken { + + private val minDragDistance: Int + private var initialTouchX = 0f + private var initialTouchY = 0f + private val touchSlop: Float + private val slopMultiplier: Float + private val temp2 = IntArray(2) + private var draggedFarEnough = false + private var startingChild: ExpandableView? = null + private var lastHeight = 0f + var isDraggingDown = false + private set + + init { + minDragDistance = context.resources.getDimensionPixelSize( + R.dimen.keyguard_drag_down_min_distance) + val configuration = ViewConfiguration.get(context) + touchSlop = configuration.scaledTouchSlop.toFloat() + slopMultiplier = configuration.scaledAmbiguousGestureMultiplier + } + + override fun onInterceptTouchEvent(event: MotionEvent): Boolean { + val x = event.x + val y = event.y + when (event.actionMasked) { + MotionEvent.ACTION_DOWN -> { + draggedFarEnough = false + isDraggingDown = false + startingChild = null + initialTouchY = y + initialTouchX = x + } + MotionEvent.ACTION_MOVE -> { + val h = y - initialTouchY + // Adjust the touch slop if another gesture may be being performed. + val touchSlop = if (event.classification + == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE) + touchSlop * slopMultiplier + else + touchSlop + if (h > touchSlop && h > Math.abs(x - initialTouchX)) { + falsingCollector.onNotificationStartDraggingDown() + isDraggingDown = true + captureStartingChild(initialTouchX, initialTouchY) + initialTouchY = y + initialTouchX = x + dragDownCallback.onTouchSlopExceeded() + return startingChild != null || dragDownCallback.isDragDownAnywhereEnabled + } + } + } + return false + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + if (!isDraggingDown) { + return false + } + val x = event.x + val y = event.y + when (event.actionMasked) { + MotionEvent.ACTION_MOVE -> { + lastHeight = y - initialTouchY + captureStartingChild(initialTouchX, initialTouchY) + if (startingChild != null) { + handleExpansion(lastHeight, startingChild!!) + } else { + dragDownCallback.setEmptyDragAmount(lastHeight) + } + if (lastHeight > minDragDistance) { + if (!draggedFarEnough) { + draggedFarEnough = true + dragDownCallback.onCrossedThreshold(true) + } + } else { + if (draggedFarEnough) { + draggedFarEnough = false + dragDownCallback.onCrossedThreshold(false) + } + } + return true + } + MotionEvent.ACTION_UP -> if (!falsingManager.isUnlockingDisabled && !isFalseTouch + && dragDownCallback.canDragDown()) { + dragDownCallback.onDraggedDown(startingChild, (y - initialTouchY).toInt()) + if (startingChild == null) { + cancelExpansion() + } else { + expandCallback.setUserLockedChild(startingChild, false) + startingChild = null + } + isDraggingDown = false + } else { + stopDragging() + return false + } + MotionEvent.ACTION_CANCEL -> { + stopDragging() + return false + } + } + return false + } + + private val isFalseTouch: Boolean + get() { + return if (!dragDownCallback.isFalsingCheckNeeded) { + false + } else { + falsingManager.isFalseTouch(Classifier.NOTIFICATION_DRAG_DOWN) || !draggedFarEnough + } + } + + private fun captureStartingChild(x: Float, y: Float) { + if (startingChild == null) { + startingChild = findView(x, y) + if (startingChild != null) { + if (dragDownCallback.isDragDownEnabledForView(startingChild)) { + expandCallback.setUserLockedChild(startingChild, true) + } else { + startingChild = null + } + } + } + } + + private fun handleExpansion(heightDelta: Float, child: ExpandableView) { + var hDelta = heightDelta + if (hDelta < 0) { + hDelta = 0f + } + val expandable = child.isContentExpandable + val rubberbandFactor = if (expandable) { + RUBBERBAND_FACTOR_EXPANDABLE + } else { + RUBBERBAND_FACTOR_STATIC + } + var rubberband = hDelta * rubberbandFactor + if (expandable && rubberband + child.collapsedHeight > child.maxContentHeight) { + var overshoot = rubberband + child.collapsedHeight - child.maxContentHeight + overshoot *= 1 - RUBBERBAND_FACTOR_STATIC + rubberband -= overshoot + } + child.actualHeight = (child.collapsedHeight + rubberband).toInt() + } + + private fun cancelExpansion(child: ExpandableView) { + if (child.actualHeight == child.collapsedHeight) { + expandCallback.setUserLockedChild(child, false) + return + } + val anim = ObjectAnimator.ofInt(child, "actualHeight", + child.actualHeight, child.collapsedHeight) + anim.interpolator = Interpolators.FAST_OUT_SLOW_IN + anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong() + anim.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + expandCallback.setUserLockedChild(child, false) + } + }) + anim.start() + } + + private fun cancelExpansion() { + val anim = ValueAnimator.ofFloat(lastHeight, 0f) + anim.interpolator = Interpolators.FAST_OUT_SLOW_IN + anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong() + anim.addUpdateListener { animation: ValueAnimator -> + dragDownCallback.setEmptyDragAmount(animation.animatedValue as Float) + } + anim.start() + } + + private fun stopDragging() { + falsingCollector.onNotificationStopDraggingDown() + if (startingChild != null) { + cancelExpansion(startingChild!!) + startingChild = null + } else { + cancelExpansion() + } + isDraggingDown = false + dragDownCallback.onDragDownReset() + } + + private fun findView(x: Float, y: Float): ExpandableView { + host.getLocationOnScreen(temp2) + return expandCallback.getChildAtRawPosition(x + temp2[0] , y + temp2[1]) + } + + val isDragDownEnabled: Boolean + get() = dragDownCallback.isDragDownEnabledForView(null) + + interface DragDownCallback { + + /** + * @return true if the interaction is accepted, false if it should be cancelled + */ + fun canDragDown(): Boolean + + /** + * Call when a view has been dragged. + **/ + fun onDraggedDown(startingChild: View?, dragLengthY: Int) + fun onDragDownReset() + + /** + * The user has dragged either above or below the threshold + * @param above whether they dragged above it + */ + fun onCrossedThreshold(above: Boolean) + fun onTouchSlopExceeded() + fun setEmptyDragAmount(amount: Float) + val isFalsingCheckNeeded: Boolean + + /** + * Is dragging down enabled on a given view + * @param view The view to check or `null` to check if it's enabled at all + */ + fun isDragDownEnabledForView(view: ExpandableView?): Boolean + + /** + * @return if drag down is enabled anywhere, not just on selected views. + */ + val isDragDownAnywhereEnabled: Boolean + } + + companion object { + private const val RUBBERBAND_FACTOR_EXPANDABLE = 0.5f + private const val RUBBERBAND_FACTOR_STATIC = 0.15f + private const val SPRING_BACK_ANIMATION_LENGTH_MS = 375 + } +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java index 4d70237b1d909..f19cf1bed0349 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java @@ -409,9 +409,8 @@ public class NotificationShadeWindowViewController { ExpandHelper.Callback expandHelperCallback = mStackScrollLayout.getExpandHelperCallback(); DragDownHelper.DragDownCallback dragDownCallback = mStackScrollLayout.getDragDownCallback(); setDragDownHelper( - new DragDownHelper( - mView.getContext(), mView, expandHelperCallback, - dragDownCallback, mFalsingManager, mFalsingCollector)); + new DragDownHelper(mFalsingManager, expandHelperCallback, dragDownCallback, + mFalsingCollector, mView, mView.getContext())); mDepthController.setRoot(mView); mNotificationPanelViewController.addExpansionListener(mDepthController);