Merge "Adding the touch gestures for the pull down notification shade." into qt-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
784999f21f
@@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
package com.android.systemui.statusbar.car;
|
package com.android.systemui.statusbar.car;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.ActivityTaskManager;
|
import android.app.ActivityTaskManager;
|
||||||
import android.car.drivingstate.CarDrivingStateEvent;
|
import android.car.drivingstate.CarDrivingStateEvent;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.GestureDetector;
|
import android.view.GestureDetector;
|
||||||
@@ -55,6 +59,7 @@ import com.android.systemui.plugins.qs.QS;
|
|||||||
import com.android.systemui.qs.car.CarQSFragment;
|
import com.android.systemui.qs.car.CarQSFragment;
|
||||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||||
|
import com.android.systemui.statusbar.FlingAnimationUtils;
|
||||||
import com.android.systemui.statusbar.StatusBarState;
|
import com.android.systemui.statusbar.StatusBarState;
|
||||||
import com.android.systemui.statusbar.car.hvac.HvacController;
|
import com.android.systemui.statusbar.car.hvac.HvacController;
|
||||||
import com.android.systemui.statusbar.car.hvac.TemperatureView;
|
import com.android.systemui.statusbar.car.hvac.TemperatureView;
|
||||||
@@ -74,6 +79,15 @@ import java.util.Map;
|
|||||||
public class CarStatusBar extends StatusBar implements
|
public class CarStatusBar extends StatusBar implements
|
||||||
CarBatteryController.BatteryViewHandler {
|
CarBatteryController.BatteryViewHandler {
|
||||||
private static final String TAG = "CarStatusBar";
|
private static final String TAG = "CarStatusBar";
|
||||||
|
// used to calculate how fast to open or close the window
|
||||||
|
private static final float DEFAULT_FLING_VELOCITY = 0;
|
||||||
|
// max time a fling animation takes
|
||||||
|
private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
|
||||||
|
// acceleration rate for the fling animation
|
||||||
|
private static final float FLING_SPEED_UP_FACTOR = 0.6f;
|
||||||
|
|
||||||
|
private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
|
||||||
|
private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
|
||||||
|
|
||||||
private TaskStackListenerImpl mTaskStackListener;
|
private TaskStackListenerImpl mTaskStackListener;
|
||||||
|
|
||||||
@@ -100,6 +114,7 @@ public class CarStatusBar extends StatusBar implements
|
|||||||
private boolean mDeviceIsProvisioned = true;
|
private boolean mDeviceIsProvisioned = true;
|
||||||
private HvacController mHvacController;
|
private HvacController mHvacController;
|
||||||
private DrivingStateHelper mDrivingStateHelper;
|
private DrivingStateHelper mDrivingStateHelper;
|
||||||
|
private static FlingAnimationUtils sFlingAnimationUtils;
|
||||||
private SwitchToGuestTimer mSwitchToGuestTimer;
|
private SwitchToGuestTimer mSwitchToGuestTimer;
|
||||||
|
|
||||||
// The container for the notifications.
|
// The container for the notifications.
|
||||||
@@ -113,6 +128,27 @@ public class CarStatusBar extends StatusBar implements
|
|||||||
// it's open.
|
// it's open.
|
||||||
private View.OnTouchListener mNavBarNotificationTouchListener;
|
private View.OnTouchListener mNavBarNotificationTouchListener;
|
||||||
|
|
||||||
|
// Percentage from top of the screen after which the notification shade will open. This value
|
||||||
|
// will be used while opening the notification shade.
|
||||||
|
private int mSettleOpenPercentage;
|
||||||
|
// Percentage from top of the screen below which the notification shade will close. This
|
||||||
|
// value will be used while closing the notification shade.
|
||||||
|
private int mSettleClosePercentage;
|
||||||
|
// Percentage of notification shade open from top of the screen.
|
||||||
|
private int mPercentageFromBottom;
|
||||||
|
// If notification shade is animation to close or to open.
|
||||||
|
private boolean mIsNotificationAnimating;
|
||||||
|
|
||||||
|
// Tracks when the notification shade is being scrolled. This refers to the glass pane being
|
||||||
|
// scrolled not the recycler view.
|
||||||
|
private boolean mIsTracking;
|
||||||
|
private float mFirstTouchDownOnGlassPane;
|
||||||
|
|
||||||
|
// If the notification card inside the recycler view is being swiped.
|
||||||
|
private boolean mIsNotificationCardSwiping;
|
||||||
|
// If notification shade is being swiped vertically to close.
|
||||||
|
private boolean mIsSwipingVerticallyToClose;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
// get the provisioned state before calling the parent class since it's that flow that
|
// get the provisioned state before calling the parent class since it's that flow that
|
||||||
@@ -125,6 +161,12 @@ public class CarStatusBar extends StatusBar implements
|
|||||||
mActivityManagerWrapper.registerTaskStackListener(mTaskStackListener);
|
mActivityManagerWrapper.registerTaskStackListener(mTaskStackListener);
|
||||||
|
|
||||||
mNotificationPanel.setScrollingEnabled(true);
|
mNotificationPanel.setScrollingEnabled(true);
|
||||||
|
mSettleOpenPercentage = mContext.getResources().getInteger(
|
||||||
|
R.integer.notification_settle_open_percentage);
|
||||||
|
mSettleClosePercentage = mContext.getResources().getInteger(
|
||||||
|
R.integer.notification_settle_close_percentage);
|
||||||
|
sFlingAnimationUtils = new FlingAnimationUtils(mContext,
|
||||||
|
FLING_ANIMATION_MAX_TIME, FLING_SPEED_UP_FACTOR);
|
||||||
|
|
||||||
createBatteryController();
|
createBatteryController();
|
||||||
mCarBatteryController.startListening();
|
mCarBatteryController.startListening();
|
||||||
@@ -313,14 +355,46 @@ public class CarStatusBar extends StatusBar implements
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
mNavBarNotificationTouchListener =
|
mNavBarNotificationTouchListener =
|
||||||
(v, event) -> navBarCloseNotificationGestureDetector.onTouchEvent(event);
|
(v, event) -> {
|
||||||
|
boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
|
||||||
|
if (consumed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (event.getActionMasked() == MotionEvent.ACTION_UP
|
||||||
|
&& mNotificationView.getVisibility() == View.VISIBLE) {
|
||||||
|
if (mSettleClosePercentage < mPercentageFromBottom) {
|
||||||
|
animateNotificationPanel(
|
||||||
|
DEFAULT_FLING_VELOCITY, false);
|
||||||
|
} else {
|
||||||
|
animateNotificationPanel(DEFAULT_FLING_VELOCITY,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
// The following are the ui elements that the user would call the status bar.
|
// The following are the ui elements that the user would call the status bar.
|
||||||
// This will set the status bar so it they can make call backs.
|
// This will set the status bar so it they can make call backs.
|
||||||
CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
|
CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
|
||||||
topBar.setStatusBar(this);
|
topBar.setStatusBar(this);
|
||||||
topBar.setStatusBarWindowTouchListener((v1, event1) ->
|
topBar.setStatusBarWindowTouchListener((v1, event1) -> {
|
||||||
openGestureDetector.onTouchEvent(event1));
|
|
||||||
|
boolean consumed = openGestureDetector.onTouchEvent(event1);
|
||||||
|
if (consumed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (event1.getActionMasked() == MotionEvent.ACTION_UP
|
||||||
|
&& mNotificationView.getVisibility() == View.VISIBLE) {
|
||||||
|
if (mSettleOpenPercentage > mPercentageFromBottom) {
|
||||||
|
animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
|
||||||
|
} else {
|
||||||
|
animateNotificationPanel(
|
||||||
|
DEFAULT_FLING_VELOCITY, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
NotificationClickHandlerFactory clickHandlerFactory = new NotificationClickHandlerFactory(
|
NotificationClickHandlerFactory clickHandlerFactory = new NotificationClickHandlerFactory(
|
||||||
mBarService,
|
mBarService,
|
||||||
@@ -347,7 +421,10 @@ public class CarStatusBar extends StatusBar implements
|
|||||||
mNotificationListAtBottomAtTimeOfTouch = false;
|
mNotificationListAtBottomAtTimeOfTouch = false;
|
||||||
}
|
}
|
||||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||||
|
mFirstTouchDownOnGlassPane = event.getRawX();
|
||||||
mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
|
mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
|
||||||
|
// Reset the tracker when there is a touch down on the glass pane.
|
||||||
|
mIsTracking = false;
|
||||||
// Pass the down event to gesture detector so that it knows where the touch event
|
// Pass the down event to gesture detector so that it knows where the touch event
|
||||||
// started.
|
// started.
|
||||||
closeGestureDetector.onTouchEvent(event);
|
closeGestureDetector.onTouchEvent(event);
|
||||||
@@ -364,23 +441,56 @@ public class CarStatusBar extends StatusBar implements
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mNotificationListAtBottom = false;
|
mNotificationListAtBottom = false;
|
||||||
|
mIsSwipingVerticallyToClose = false;
|
||||||
mNotificationListAtBottomAtTimeOfTouch = false;
|
mNotificationListAtBottomAtTimeOfTouch = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mNotificationList.setOnTouchListener(new View.OnTouchListener() {
|
mNotificationList.setOnTouchListener(new View.OnTouchListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
mIsNotificationCardSwiping = Math.abs(mFirstTouchDownOnGlassPane - event.getRawX())
|
||||||
|
> SWIPE_MAX_OFF_PATH;
|
||||||
|
if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
|
||||||
|
// We need to save the state here as if notification card is swiping we will
|
||||||
|
// change the mNotificationListAtBottomAtTimeOfTouch. This is to protect
|
||||||
|
// closing the notification shade while the notification card is being swiped.
|
||||||
|
mIsSwipingVerticallyToClose = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the card is swiping we should not allow the notification shade to close.
|
||||||
|
// Hence setting mNotificationListAtBottomAtTimeOfTouch to false will stop that
|
||||||
|
// for us. We are also checking for mIsTracking because while swiping the
|
||||||
|
// notification shade to close if the user goes a bit horizontal while swiping
|
||||||
|
// upwards then also this should close.
|
||||||
|
if (mIsNotificationCardSwiping && !mIsTracking) {
|
||||||
|
mNotificationListAtBottomAtTimeOfTouch = false;
|
||||||
|
}
|
||||||
|
|
||||||
boolean handled = false;
|
boolean handled = false;
|
||||||
if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
|
if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
|
||||||
handled = closeGestureDetector.onTouchEvent(event);
|
handled = closeGestureDetector.onTouchEvent(event);
|
||||||
}
|
}
|
||||||
|
boolean isTracking = mIsTracking;
|
||||||
|
Rect rect = mNotificationView.getClipBounds();
|
||||||
|
float clippedHeight = rect.bottom;
|
||||||
|
if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP
|
||||||
|
&& mIsSwipingVerticallyToClose) {
|
||||||
|
if (mSettleClosePercentage < mPercentageFromBottom && isTracking) {
|
||||||
|
animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
|
||||||
|
} else if (clippedHeight != mNotificationView.getHeight() && isTracking) {
|
||||||
|
// this can be caused when user is at the end of the list and trying to
|
||||||
|
// fling to top of the list by scrolling down.
|
||||||
|
animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after
|
// Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after
|
||||||
// the event has been passed to the closeGestureDetector above, such that the
|
// the event has been passed to the closeGestureDetector above, such that the
|
||||||
// closeGestureDetector sees the up event before the state has changed.
|
// closeGestureDetector sees the up event before the state has changed.
|
||||||
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
|
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
|
||||||
mNotificationListAtBottomAtTimeOfTouch = false;
|
mNotificationListAtBottomAtTimeOfTouch = false;
|
||||||
}
|
}
|
||||||
return handled;
|
return handled || isTracking;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -401,7 +511,9 @@ public class CarStatusBar extends StatusBar implements
|
|||||||
mNotificationList.scrollToPosition(0);
|
mNotificationList.scrollToPosition(0);
|
||||||
mStatusBarWindowController.setPanelVisible(true);
|
mStatusBarWindowController.setPanelVisible(true);
|
||||||
mNotificationView.setVisibility(View.VISIBLE);
|
mNotificationView.setVisibility(View.VISIBLE);
|
||||||
// let the status bar know that the panel is open
|
|
||||||
|
animateNotificationPanel(mOpeningVelocity, false);
|
||||||
|
|
||||||
setPanelExpanded(true);
|
setPanelExpanded(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,12 +527,66 @@ public class CarStatusBar extends StatusBar implements
|
|||||||
mStatusBarWindowController.setStatusBarFocusable(false);
|
mStatusBarWindowController.setStatusBarFocusable(false);
|
||||||
mStatusBarWindow.cancelExpandHelper();
|
mStatusBarWindow.cancelExpandHelper();
|
||||||
mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
|
mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
|
||||||
mStatusBarWindowController.setPanelVisible(false);
|
|
||||||
mNotificationView.setVisibility(View.INVISIBLE);
|
animateNotificationPanel(mClosingVelocity, true);
|
||||||
// let the status bar know that the panel is cloased
|
|
||||||
|
if (!mIsTracking) {
|
||||||
|
mStatusBarWindowController.setPanelVisible(false);
|
||||||
|
mNotificationView.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
setPanelExpanded(false);
|
setPanelExpanded(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animates the notification shade from one position to other. This is used to either open or
|
||||||
|
* close the notification shade completely with a velocity. Id the animation is to close the
|
||||||
|
* notification shade this method also makes the view invisible after animation ends.
|
||||||
|
*/
|
||||||
|
private void animateNotificationPanel(float velocity, boolean isClosing) {
|
||||||
|
Rect rect = mNotificationView.getClipBounds();
|
||||||
|
if (rect == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float from = rect.bottom;
|
||||||
|
float to = 0;
|
||||||
|
if (!isClosing) {
|
||||||
|
to = mNotificationView.getHeight();
|
||||||
|
}
|
||||||
|
if (mIsNotificationAnimating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mIsNotificationAnimating = true;
|
||||||
|
mIsTracking = true;
|
||||||
|
ValueAnimator animator = ValueAnimator.ofFloat(from, to);
|
||||||
|
animator.addUpdateListener(
|
||||||
|
animation -> {
|
||||||
|
float animatedValue = (Float) animation.getAnimatedValue();
|
||||||
|
setNotificationViewClipBounds((int) animatedValue);
|
||||||
|
});
|
||||||
|
animator.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
super.onAnimationEnd(animation);
|
||||||
|
mIsNotificationAnimating = false;
|
||||||
|
mIsTracking = false;
|
||||||
|
mOpeningVelocity = DEFAULT_FLING_VELOCITY;
|
||||||
|
mClosingVelocity = DEFAULT_FLING_VELOCITY;
|
||||||
|
if (isClosing) {
|
||||||
|
mStatusBarWindowController.setPanelVisible(false);
|
||||||
|
mNotificationView.setVisibility(View.INVISIBLE);
|
||||||
|
mNotificationView.setClipBounds(null);
|
||||||
|
// let the status bar know that the panel is closed
|
||||||
|
setPanelExpanded(false);
|
||||||
|
} else {
|
||||||
|
// let the status bar know that the panel is open
|
||||||
|
setPanelExpanded(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity));
|
||||||
|
animator.start();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected QS createDefaultQSFragment() {
|
protected QS createDefaultQSFragment() {
|
||||||
@@ -576,7 +742,7 @@ public class CarStatusBar extends StatusBar implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||||
//When executing dump() funciton simultaneously, we need to serialize them
|
//When executing dump() function simultaneously, we need to serialize them
|
||||||
//to get mStackScroller's position correctly.
|
//to get mStackScroller's position correctly.
|
||||||
synchronized (mQueueLock) {
|
synchronized (mQueueLock) {
|
||||||
pw.println(" mStackScroller: " + viewInfo(mStackScroller));
|
pw.println(" mStackScroller: " + viewInfo(mStackScroller));
|
||||||
@@ -760,38 +926,69 @@ public class CarStatusBar extends StatusBar implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if the current user makes it through the setup wizard, false otherwise. */
|
/** Returns true if the current user makes it through the setup wizard, false otherwise. */
|
||||||
public boolean getIsUserSetup(){
|
private boolean getIsUserSetup() {
|
||||||
return mUserSetup;
|
return mUserSetup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setNotificationViewClipBounds(int height) {
|
||||||
|
Rect clipBounds = new Rect();
|
||||||
|
clipBounds.set(0, 0, mNotificationView.getWidth(), height);
|
||||||
|
mNotificationView.setClipBounds(clipBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculatePercentageFromBottom(float height) {
|
||||||
|
if (mNotificationView.getHeight() > 0) {
|
||||||
|
mPercentageFromBottom = (int) Math.abs(
|
||||||
|
height / mNotificationView.getHeight() * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: add settle down/up logic
|
|
||||||
private static final int SWIPE_UP_MIN_DISTANCE = 75;
|
private static final int SWIPE_UP_MIN_DISTANCE = 75;
|
||||||
private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
|
private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
|
||||||
private static final int SWIPE_MAX_OFF_PATH = 75;
|
private static final int SWIPE_MAX_OFF_PATH = 75;
|
||||||
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
|
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
|
||||||
|
|
||||||
// Only responsible for open hooks. Since once the panel opens it covers all elements
|
// Only responsible for open hooks. Since once the panel opens it covers all elements
|
||||||
// there is no need to merge with close.
|
// there is no need to merge with close.
|
||||||
private abstract class OpenNotificationGestureListener extends
|
private abstract class OpenNotificationGestureListener extends
|
||||||
GestureDetector.SimpleOnGestureListener {
|
GestureDetector.SimpleOnGestureListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
|
||||||
|
float distanceY) {
|
||||||
|
|
||||||
|
if (mNotificationView.getVisibility() == View.INVISIBLE) {
|
||||||
|
// when the on-scroll is called for the first time to open.
|
||||||
|
mNotificationList.scrollToPosition(0);
|
||||||
|
}
|
||||||
|
mStatusBarWindowController.setPanelVisible(true);
|
||||||
|
mNotificationView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
// clips the view for the notification shade when the user scrolls to open.
|
||||||
|
setNotificationViewClipBounds((int) event2.getRawY());
|
||||||
|
|
||||||
|
// Initially the scroll starts with height being zero. This checks protects from divide
|
||||||
|
// by zero error.
|
||||||
|
calculatePercentageFromBottom(event2.getRawY());
|
||||||
|
|
||||||
|
mIsTracking = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onFling(MotionEvent event1, MotionEvent event2,
|
public boolean onFling(MotionEvent event1, MotionEvent event2,
|
||||||
float velocityX, float velocityY) {
|
float velocityX, float velocityY) {
|
||||||
if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
|
if (velocityY > SWIPE_THRESHOLD_VELOCITY) {
|
||||||
|| Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
|
mOpeningVelocity = velocityY;
|
||||||
// swipe was not vertical or was not fast enough
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
boolean isDown = velocityY > 0;
|
|
||||||
float distanceDelta = Math.abs(event1.getY() - event2.getY());
|
|
||||||
if (isDown && distanceDelta > SWIPE_DOWN_MIN_DISTANCE) {
|
|
||||||
openNotification();
|
openNotification();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void openNotification();
|
protected abstract void openNotification();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -799,36 +996,85 @@ public class CarStatusBar extends StatusBar implements
|
|||||||
private abstract class CloseNotificationGestureListener extends
|
private abstract class CloseNotificationGestureListener extends
|
||||||
GestureDetector.SimpleOnGestureListener {
|
GestureDetector.SimpleOnGestureListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
|
||||||
|
float distanceY) {
|
||||||
|
if (!mNotificationListAtBottomAtTimeOfTouch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
float actualNotificationHeight =
|
||||||
|
mNotificationView.getHeight() - (event1.getRawY() - event2.getRawY());
|
||||||
|
if (actualNotificationHeight > mNotificationView.getHeight()) {
|
||||||
|
actualNotificationHeight = mNotificationView.getHeight();
|
||||||
|
}
|
||||||
|
if (mNotificationView.getHeight() > 0) {
|
||||||
|
mPercentageFromBottom = (int) Math.abs(
|
||||||
|
actualNotificationHeight / mNotificationView.getHeight() * 100);
|
||||||
|
boolean isUp = distanceY > 0;
|
||||||
|
|
||||||
|
// This check is to figure out if onScroll was called while swiping the card at
|
||||||
|
// bottom of the list. At that time we should not allow notification shade to
|
||||||
|
// close. We are also checking for the upwards swipe gesture here because it is
|
||||||
|
// possible if a user is closing the notification shade and while swiping starts
|
||||||
|
// to open again but does not fling. At that time we should allow the
|
||||||
|
// notification shade to close fully or else it would stuck in between.
|
||||||
|
if (Math.abs(mNotificationView.getHeight() - actualNotificationHeight)
|
||||||
|
> SWIPE_DOWN_MIN_DISTANCE && isUp) {
|
||||||
|
setNotificationViewClipBounds((int) actualNotificationHeight);
|
||||||
|
mIsTracking = true;
|
||||||
|
} else if (!isUp) {
|
||||||
|
setNotificationViewClipBounds((int) actualNotificationHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we return true the the items in RV won't be scrollable.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onFling(MotionEvent event1, MotionEvent event2,
|
public boolean onFling(MotionEvent event1, MotionEvent event2,
|
||||||
float velocityX, float velocityY) {
|
float velocityX, float velocityY) {
|
||||||
|
|
||||||
if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
|
if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
|
||||||
|| Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
|
|| Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
|
||||||
// swipe was not vertical or was not fast enough
|
// swipe was not vertical or was not fast enough
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean isUp = velocityY < 0;
|
boolean isUp = velocityY < 0;
|
||||||
float distanceDelta = Math.abs(event1.getY() - event2.getY());
|
if (isUp) {
|
||||||
if (isUp && distanceDelta > SWIPE_UP_MIN_DISTANCE) {
|
|
||||||
close();
|
close();
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
// we should close the shade
|
||||||
|
animateNotificationPanel(velocityY, false);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void close();
|
protected abstract void close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// to be installed on the nav bars
|
// To be installed on the nav bars.
|
||||||
private abstract class NavBarCloseNotificationGestureListener extends
|
private abstract class NavBarCloseNotificationGestureListener extends
|
||||||
CloseNotificationGestureListener {
|
CloseNotificationGestureListener {
|
||||||
@Override
|
@Override
|
||||||
public boolean onSingleTapUp(MotionEvent e) {
|
public boolean onSingleTapUp(MotionEvent e) {
|
||||||
|
mClosingVelocity = DEFAULT_FLING_VELOCITY;
|
||||||
close();
|
close();
|
||||||
return super.onSingleTapUp(e);
|
return super.onSingleTapUp(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
|
||||||
|
float distanceY) {
|
||||||
|
calculatePercentageFromBottom(event2.getRawY());
|
||||||
|
setNotificationViewClipBounds((int) event2.getRawY());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLongPress(MotionEvent e) {
|
public void onLongPress(MotionEvent e) {
|
||||||
|
mClosingVelocity = DEFAULT_FLING_VELOCITY;
|
||||||
close();
|
close();
|
||||||
super.onLongPress(e);
|
super.onLongPress(e);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user