Merge "[SystemUI] Add notification to window feature." into sc-v2-dev
This commit is contained in:
@@ -671,4 +671,7 @@
|
|||||||
1 - Override the setting to always bypass keyguard
|
1 - Override the setting to always bypass keyguard
|
||||||
2 - Override the setting to never bypass keyguard -->
|
2 - Override the setting to never bypass keyguard -->
|
||||||
<integer name="config_face_unlock_bypass_override">0</integer>
|
<integer name="config_face_unlock_bypass_override">0</integer>
|
||||||
|
|
||||||
|
<!-- Flag to activate notification to contents feature -->
|
||||||
|
<bool name="config_notificationToContents">false</bool>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1594,4 +1594,6 @@
|
|||||||
<!-- The padding between the icon and the text. -->
|
<!-- The padding between the icon and the text. -->
|
||||||
<dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
|
<dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
|
||||||
<dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
|
<dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="drag_and_drop_icon_size">70dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import android.animation.ValueAnimator;
|
|||||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -73,6 +75,9 @@ public class SwipeHelper implements Gefingerpoken {
|
|||||||
private final FlingAnimationUtils mFlingAnimationUtils;
|
private final FlingAnimationUtils mFlingAnimationUtils;
|
||||||
private float mPagingTouchSlop;
|
private float mPagingTouchSlop;
|
||||||
private final float mSlopMultiplier;
|
private final float mSlopMultiplier;
|
||||||
|
private int mTouchSlop;
|
||||||
|
private float mTouchSlopMultiplier;
|
||||||
|
|
||||||
private final Callback mCallback;
|
private final Callback mCallback;
|
||||||
private final int mSwipeDirection;
|
private final int mSwipeDirection;
|
||||||
private final VelocityTracker mVelocityTracker;
|
private final VelocityTracker mVelocityTracker;
|
||||||
@@ -105,6 +110,10 @@ public class SwipeHelper implements Gefingerpoken {
|
|||||||
final int y = (int) mDownLocation[1] - mViewOffset[1];
|
final int y = (int) mDownLocation[1] - mViewOffset[1];
|
||||||
mTouchedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
|
mTouchedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
|
||||||
((ExpandableNotificationRow) mTouchedView).doLongClickCallback(x, y);
|
((ExpandableNotificationRow) mTouchedView).doLongClickCallback(x, y);
|
||||||
|
|
||||||
|
if (isAvailableToDragAndDrop(mTouchedView)) {
|
||||||
|
mCallback.onLongPressSent(mTouchedView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,6 +135,8 @@ public class SwipeHelper implements Gefingerpoken {
|
|||||||
mVelocityTracker = VelocityTracker.obtain();
|
mVelocityTracker = VelocityTracker.obtain();
|
||||||
mPagingTouchSlop = viewConfiguration.getScaledPagingTouchSlop();
|
mPagingTouchSlop = viewConfiguration.getScaledPagingTouchSlop();
|
||||||
mSlopMultiplier = viewConfiguration.getScaledAmbiguousGestureMultiplier();
|
mSlopMultiplier = viewConfiguration.getScaledAmbiguousGestureMultiplier();
|
||||||
|
mTouchSlop = viewConfiguration.getScaledTouchSlop();
|
||||||
|
mTouchSlopMultiplier = viewConfiguration.getAmbiguousGestureMultiplier();
|
||||||
|
|
||||||
// Extra long-press!
|
// Extra long-press!
|
||||||
mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
|
mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
|
||||||
@@ -297,7 +308,9 @@ public class SwipeHelper implements Gefingerpoken {
|
|||||||
mIsSwiping = false;
|
mIsSwiping = false;
|
||||||
mSnappingChild = false;
|
mSnappingChild = false;
|
||||||
mLongPressSent = false;
|
mLongPressSent = false;
|
||||||
|
mCallback.onLongPressSent(null);
|
||||||
mVelocityTracker.clear();
|
mVelocityTracker.clear();
|
||||||
|
cancelLongPress();
|
||||||
mTouchedView = mCallback.getChildAtPosition(ev);
|
mTouchedView = mCallback.getChildAtPosition(ev);
|
||||||
|
|
||||||
if (mTouchedView != null) {
|
if (mTouchedView != null) {
|
||||||
@@ -349,6 +362,7 @@ public class SwipeHelper implements Gefingerpoken {
|
|||||||
mIsSwiping = false;
|
mIsSwiping = false;
|
||||||
mTouchedView = null;
|
mTouchedView = null;
|
||||||
mLongPressSent = false;
|
mLongPressSent = false;
|
||||||
|
mCallback.onLongPressSent(null);
|
||||||
mMenuRowIntercepting = false;
|
mMenuRowIntercepting = false;
|
||||||
cancelLongPress();
|
cancelLongPress();
|
||||||
if (captured) return true;
|
if (captured) return true;
|
||||||
@@ -593,11 +607,7 @@ public class SwipeHelper implements Gefingerpoken {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouchEvent(MotionEvent ev) {
|
public boolean onTouchEvent(MotionEvent ev) {
|
||||||
if (mLongPressSent && !mMenuRowIntercepting) {
|
if (!mIsSwiping && !mMenuRowIntercepting && !mLongPressSent) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mIsSwiping && !mMenuRowIntercepting) {
|
|
||||||
if (mCallback.getChildAtPosition(ev) != null) {
|
if (mCallback.getChildAtPosition(ev) != null) {
|
||||||
// We are dragging directly over a card, make sure that we also catch the gesture
|
// We are dragging directly over a card, make sure that we also catch the gesture
|
||||||
// even if nobody else wants the touch event.
|
// even if nobody else wants the touch event.
|
||||||
@@ -623,30 +633,40 @@ public class SwipeHelper implements Gefingerpoken {
|
|||||||
if (absDelta >= getFalsingThreshold()) {
|
if (absDelta >= getFalsingThreshold()) {
|
||||||
mTouchAboveFalsingThreshold = true;
|
mTouchAboveFalsingThreshold = true;
|
||||||
}
|
}
|
||||||
// don't let items that can't be dismissed be dragged more than
|
|
||||||
// maxScrollDistance
|
if (mLongPressSent) {
|
||||||
if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(
|
if (absDelta >= getTouchSlop(ev)) {
|
||||||
mTouchedView,
|
if (mTouchedView instanceof ExpandableNotificationRow) {
|
||||||
delta > 0)) {
|
((ExpandableNotificationRow) mTouchedView)
|
||||||
float size = getSize(mTouchedView);
|
.doDragCallback(ev.getX(), ev.getY());
|
||||||
float maxScrollDistance = MAX_SCROLL_SIZE_FRACTION * size;
|
}
|
||||||
if (absDelta >= size) {
|
}
|
||||||
delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
|
} else {
|
||||||
} else {
|
// don't let items that can't be dismissed be dragged more than
|
||||||
int startPosition = mCallback.getConstrainSwipeStartPosition();
|
// maxScrollDistance
|
||||||
if (absDelta > startPosition) {
|
if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(
|
||||||
int signedStartPosition =
|
mTouchedView,
|
||||||
(int) (startPosition * Math.signum(delta));
|
delta > 0)) {
|
||||||
delta = signedStartPosition
|
float size = getSize(mTouchedView);
|
||||||
+ maxScrollDistance * (float) Math.sin(
|
float maxScrollDistance = MAX_SCROLL_SIZE_FRACTION * size;
|
||||||
((delta - signedStartPosition) / size) * (Math.PI / 2));
|
if (absDelta >= size) {
|
||||||
|
delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
|
||||||
|
} else {
|
||||||
|
int startPosition = mCallback.getConstrainSwipeStartPosition();
|
||||||
|
if (absDelta > startPosition) {
|
||||||
|
int signedStartPosition =
|
||||||
|
(int) (startPosition * Math.signum(delta));
|
||||||
|
delta = signedStartPosition
|
||||||
|
+ maxScrollDistance * (float) Math.sin(
|
||||||
|
((delta - signedStartPosition) / size) * (Math.PI / 2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
setTranslation(mTouchedView, mTranslation + delta);
|
setTranslation(mTouchedView, mTranslation + delta);
|
||||||
updateSwipeProgressFromOffset(mTouchedView, mCanCurrViewBeDimissed);
|
updateSwipeProgressFromOffset(mTouchedView, mCanCurrViewBeDimissed);
|
||||||
onMoveUpdate(mTouchedView, ev, mTranslation + delta, delta);
|
onMoveUpdate(mTouchedView, ev, mTranslation + delta, delta);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_UP:
|
case MotionEvent.ACTION_UP:
|
||||||
@@ -747,6 +767,29 @@ public class SwipeHelper implements Gefingerpoken {
|
|||||||
mIsSwiping = false;
|
mIsSwiping = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float getTouchSlop(MotionEvent event) {
|
||||||
|
// Adjust the touch slop if another gesture may be being performed.
|
||||||
|
return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
|
||||||
|
? mTouchSlop * mTouchSlopMultiplier
|
||||||
|
: mTouchSlop;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAvailableToDragAndDrop(View v) {
|
||||||
|
if (v.getResources().getBoolean(R.bool.config_notificationToContents)) {
|
||||||
|
if (v instanceof ExpandableNotificationRow) {
|
||||||
|
ExpandableNotificationRow enr = (ExpandableNotificationRow) v;
|
||||||
|
boolean canBubble = enr.getEntry().canBubble();
|
||||||
|
Notification notif = enr.getEntry().getSbn().getNotification();
|
||||||
|
PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
|
||||||
|
: notif.fullScreenIntent;
|
||||||
|
if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
View getChildAtPosition(MotionEvent ev);
|
View getChildAtPosition(MotionEvent ev);
|
||||||
|
|
||||||
@@ -770,6 +813,13 @@ public class SwipeHelper implements Gefingerpoken {
|
|||||||
|
|
||||||
void onDragCancelled(View v);
|
void onDragCancelled(View v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the child is long pressed and available to start drag and drop.
|
||||||
|
*
|
||||||
|
* @param v the view that was long pressed.
|
||||||
|
*/
|
||||||
|
void onLongPressSent(View v);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the child is snapped to a position.
|
* Called when the child is snapped to a position.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.content.Intent;
|
|||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,6 +38,9 @@ public interface NotificationActivityStarter {
|
|||||||
/** Called when the user clicks "Manage" or "History" in the Shade. */
|
/** Called when the user clicks "Manage" or "History" in the Shade. */
|
||||||
void startHistoryIntent(View view, boolean showHistory);
|
void startHistoryIntent(View view, boolean showHistory);
|
||||||
|
|
||||||
|
/** Called when the user succeed to drop notification to proper target view. */
|
||||||
|
void onDragSuccess(NotificationEntry entry);
|
||||||
|
|
||||||
default boolean isCollapsingToShowActivityOverLockscreen() {
|
default boolean isCollapsingToShowActivityOverLockscreen() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,14 @@ public final class NotificationClicker implements View.OnClickListener {
|
|||||||
private final Optional<Bubbles> mBubblesOptional;
|
private final Optional<Bubbles> mBubblesOptional;
|
||||||
private final NotificationActivityStarter mNotificationActivityStarter;
|
private final NotificationActivityStarter mNotificationActivityStarter;
|
||||||
|
|
||||||
|
private ExpandableNotificationRow.OnDragSuccessListener mOnDragSuccessListener =
|
||||||
|
new ExpandableNotificationRow.OnDragSuccessListener() {
|
||||||
|
@Override
|
||||||
|
public void onDragSuccess(NotificationEntry entry) {
|
||||||
|
mNotificationActivityStarter.onDragSuccess(entry);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private NotificationClicker(
|
private NotificationClicker(
|
||||||
NotificationClickerLogger logger,
|
NotificationClickerLogger logger,
|
||||||
Optional<StatusBar> statusBarOptional,
|
Optional<StatusBar> statusBarOptional,
|
||||||
@@ -111,8 +119,10 @@ public final class NotificationClicker implements View.OnClickListener {
|
|||||||
if (notification.contentIntent != null || notification.fullScreenIntent != null
|
if (notification.contentIntent != null || notification.fullScreenIntent != null
|
||||||
|| row.getEntry().isBubble()) {
|
|| row.getEntry().isBubble()) {
|
||||||
row.setOnClickListener(this);
|
row.setOnClickListener(this);
|
||||||
|
row.setOnDragSuccessListener(mOnDragSuccessListener);
|
||||||
} else {
|
} else {
|
||||||
row.setOnClickListener(null);
|
row.setOnClickListener(null);
|
||||||
|
row.setOnDragSuccessListener(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter;
|
|||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.MathUtils;
|
import android.util.MathUtils;
|
||||||
@@ -139,6 +140,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
|||||||
private boolean mIsHeadsUpAnimation;
|
private boolean mIsHeadsUpAnimation;
|
||||||
private int mHeadsUpAddStartLocation;
|
private int mHeadsUpAddStartLocation;
|
||||||
private float mHeadsUpLocation;
|
private float mHeadsUpLocation;
|
||||||
|
/* In order to track headsup longpress coorindate. */
|
||||||
|
protected Point mTargetPoint;
|
||||||
private boolean mIsAppearing;
|
private boolean mIsAppearing;
|
||||||
private boolean mDismissed;
|
private boolean mDismissed;
|
||||||
private boolean mRefocusOnDismiss;
|
private boolean mRefocusOnDismiss;
|
||||||
@@ -568,8 +571,19 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
|||||||
final int actualHeight = getActualHeight();
|
final int actualHeight = getActualHeight();
|
||||||
float bottom = actualHeight * interpolatedFraction;
|
float bottom = actualHeight * interpolatedFraction;
|
||||||
|
|
||||||
setOutlineRect(0, mAppearAnimationTranslation, getWidth(),
|
if (mTargetPoint != null) {
|
||||||
bottom + mAppearAnimationTranslation);
|
int width = getWidth();
|
||||||
|
float fraction = 1 - mAppearAnimationFraction;
|
||||||
|
|
||||||
|
setOutlineRect(mTargetPoint.x * fraction,
|
||||||
|
mAnimationTranslationY
|
||||||
|
+ (mAnimationTranslationY - mTargetPoint.y) * fraction,
|
||||||
|
width - (width - mTargetPoint.x) * fraction,
|
||||||
|
actualHeight - (actualHeight - mTargetPoint.y) * fraction);
|
||||||
|
} else {
|
||||||
|
setOutlineRect(0, mAppearAnimationTranslation, getWidth(),
|
||||||
|
bottom + mAppearAnimationTranslation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float getInterpolatedAppearAnimationFraction() {
|
private float getInterpolatedAppearAnimationFraction() {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import android.content.res.Configuration;
|
|||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||||
import android.graphics.drawable.AnimationDrawable;
|
import android.graphics.drawable.AnimationDrawable;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
@@ -260,6 +261,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
// Use #setLongPressPosition to optionally assign positional data with the long press.
|
// Use #setLongPressPosition to optionally assign positional data with the long press.
|
||||||
private LongPressListener mLongPressListener;
|
private LongPressListener mLongPressListener;
|
||||||
|
|
||||||
|
private ExpandableNotificationRowDragController mDragController;
|
||||||
|
|
||||||
private boolean mGroupExpansionChanging;
|
private boolean mGroupExpansionChanging;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -331,6 +334,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
private OnClickListener mOnClickListener;
|
private OnClickListener mOnClickListener;
|
||||||
|
private OnDragSuccessListener mOnDragSuccessListener;
|
||||||
private boolean mHeadsupDisappearRunning;
|
private boolean mHeadsupDisappearRunning;
|
||||||
private View mChildAfterViewWhenDismissed;
|
private View mChildAfterViewWhenDismissed;
|
||||||
private View mGroupParentWhenDismissed;
|
private View mGroupParentWhenDismissed;
|
||||||
@@ -1083,6 +1087,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
mLongPressListener = longPressListener;
|
mLongPressListener = longPressListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDragController(ExpandableNotificationRowDragController dragController) {
|
||||||
|
mDragController = dragController;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOnClickListener(@Nullable OnClickListener l) {
|
public void setOnClickListener(@Nullable OnClickListener l) {
|
||||||
super.setOnClickListener(l);
|
super.setOnClickListener(l);
|
||||||
@@ -1329,6 +1337,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
public void dismiss(boolean refocusOnDismiss) {
|
public void dismiss(boolean refocusOnDismiss) {
|
||||||
super.dismiss(refocusOnDismiss);
|
super.dismiss(refocusOnDismiss);
|
||||||
setLongPressListener(null);
|
setLongPressListener(null);
|
||||||
|
setDragController(null);
|
||||||
mGroupParentWhenDismissed = mNotificationParent;
|
mGroupParentWhenDismissed = mNotificationParent;
|
||||||
mChildAfterViewWhenDismissed = null;
|
mChildAfterViewWhenDismissed = null;
|
||||||
mEntry.getIcons().getStatusBarIcon().setDismissed();
|
mEntry.getIcons().getStatusBarIcon().setDismissed();
|
||||||
@@ -1637,6 +1646,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
}
|
}
|
||||||
onHeightReset();
|
onHeightReset();
|
||||||
requestLayout();
|
requestLayout();
|
||||||
|
|
||||||
|
setTargetPoint(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showFeedbackIcon(boolean show, Pair<Integer, Integer> resIds) {
|
public void showFeedbackIcon(boolean show, Pair<Integer, Integer> resIds) {
|
||||||
@@ -1727,6 +1738,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
mTranslateableViews.remove(mGutsStub);
|
mTranslateableViews.remove(mGutsStub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called once when starting drag motion after opening notification guts,
|
||||||
|
* in case of notification that has {@link android.app.Notification#contentIntent}
|
||||||
|
* and it is to start an activity.
|
||||||
|
*/
|
||||||
|
public void doDragCallback(float x, float y) {
|
||||||
|
if (mDragController != null) {
|
||||||
|
setTargetPoint(new Point((int) x, (int) y));
|
||||||
|
mDragController.startDragAndDrop(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnDragSuccessListener(OnDragSuccessListener listener) {
|
||||||
|
mOnDragSuccessListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a notification is dropped on proper target window.
|
||||||
|
*/
|
||||||
|
public void dragAndDropSuccess() {
|
||||||
|
mOnDragSuccessListener.onDragSuccess(getEntry());
|
||||||
|
}
|
||||||
|
|
||||||
private void doLongClickCallback() {
|
private void doLongClickCallback() {
|
||||||
doLongClickCallback(getWidth() / 2, getHeight() / 2);
|
doLongClickCallback(getWidth() / 2, getHeight() / 2);
|
||||||
}
|
}
|
||||||
@@ -3254,6 +3288,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
boolean onLongPress(View v, int x, int y, MenuItem item);
|
boolean onLongPress(View v, int x, int y, MenuItem item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when notification drag and drop is finished successfully.
|
||||||
|
*/
|
||||||
|
public interface OnDragSuccessListener {
|
||||||
|
/**
|
||||||
|
* @param entry NotificationEntry that succeed to drop on proper target window.
|
||||||
|
*/
|
||||||
|
void onDragSuccess(NotificationEntry entry);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Equivalent to View.OnClickListener with coordinates
|
* Equivalent to View.OnClickListener with coordinates
|
||||||
*/
|
*/
|
||||||
@@ -3321,4 +3365,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setTargetPoint(Point p) {
|
||||||
|
mTargetPoint = p;
|
||||||
|
}
|
||||||
|
public Point getTargetPoint() {
|
||||||
|
return mTargetPoint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
import com.android.systemui.plugins.FalsingManager;
|
import com.android.systemui.plugins.FalsingManager;
|
||||||
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||||
@@ -85,6 +86,8 @@ public class ExpandableNotificationRowController implements NodeController {
|
|||||||
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
|
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
|
||||||
private final Optional<BubblesManager> mBubblesManagerOptional;
|
private final Optional<BubblesManager> mBubblesManagerOptional;
|
||||||
|
|
||||||
|
private final ExpandableNotificationRowDragController mDragController;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ExpandableNotificationRowController(
|
public ExpandableNotificationRowController(
|
||||||
ExpandableNotificationRow view,
|
ExpandableNotificationRow view,
|
||||||
@@ -109,7 +112,8 @@ public class ExpandableNotificationRowController implements NodeController {
|
|||||||
FalsingManager falsingManager,
|
FalsingManager falsingManager,
|
||||||
FalsingCollector falsingCollector,
|
FalsingCollector falsingCollector,
|
||||||
PeopleNotificationIdentifier peopleNotificationIdentifier,
|
PeopleNotificationIdentifier peopleNotificationIdentifier,
|
||||||
Optional<BubblesManager> bubblesManagerOptional) {
|
Optional<BubblesManager> bubblesManagerOptional,
|
||||||
|
ExpandableNotificationRowDragController dragController) {
|
||||||
mView = view;
|
mView = view;
|
||||||
mListContainer = listContainer;
|
mListContainer = listContainer;
|
||||||
mActivatableNotificationViewController = activatableNotificationViewController;
|
mActivatableNotificationViewController = activatableNotificationViewController;
|
||||||
@@ -134,6 +138,7 @@ public class ExpandableNotificationRowController implements NodeController {
|
|||||||
mFalsingCollector = falsingCollector;
|
mFalsingCollector = falsingCollector;
|
||||||
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
|
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
|
||||||
mBubblesManagerOptional = bubblesManagerOptional;
|
mBubblesManagerOptional = bubblesManagerOptional;
|
||||||
|
mDragController = dragController;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -164,6 +169,10 @@ public class ExpandableNotificationRowController implements NodeController {
|
|||||||
);
|
);
|
||||||
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
||||||
if (mAllowLongPress) {
|
if (mAllowLongPress) {
|
||||||
|
if (mView.getResources().getBoolean(R.bool.config_notificationToContents)) {
|
||||||
|
mView.setDragController(mDragController);
|
||||||
|
}
|
||||||
|
|
||||||
mView.setLongPressListener((v, x, y, item) -> {
|
mView.setLongPressListener((v, x, y, item) -> {
|
||||||
if (mView.isSummaryWithChildren()) {
|
if (mView.isSummaryWithChildren()) {
|
||||||
mView.expandNotification();
|
mView.expandNotification();
|
||||||
|
|||||||
@@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* 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.notification.row;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipDescription;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.service.notification.StatusBarNotification;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.DragEvent;
|
||||||
|
import android.view.HapticFeedbackConstants;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.android.systemui.Dependency;
|
||||||
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.statusbar.CommandQueue;
|
||||||
|
import com.android.systemui.statusbar.phone.ShadeController;
|
||||||
|
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for Notification to window.
|
||||||
|
*/
|
||||||
|
public class ExpandableNotificationRowDragController {
|
||||||
|
private static final String TAG = ExpandableNotificationRowDragController.class.getSimpleName();
|
||||||
|
private int mIconSize;
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final HeadsUpManager mHeadsUpManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ExpandableNotificationRowDragController(Context context,
|
||||||
|
HeadsUpManager headsUpManager) {
|
||||||
|
mContext = context;
|
||||||
|
mHeadsUpManager = headsUpManager;
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.drag_and_drop_icon_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when drag event beyond the touchslop,
|
||||||
|
* and start drag and drop.
|
||||||
|
*
|
||||||
|
* @param view notification that was long pressed and started to drag and drop.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public void startDragAndDrop(View view) {
|
||||||
|
ExpandableNotificationRow enr = null;
|
||||||
|
if (view instanceof ExpandableNotificationRow) {
|
||||||
|
enr = (ExpandableNotificationRow) view;
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBarNotification sn = enr.getEntry().getSbn();
|
||||||
|
Notification notification = sn.getNotification();
|
||||||
|
final PendingIntent contentIntent = notification.contentIntent != null
|
||||||
|
? notification.contentIntent
|
||||||
|
: notification.fullScreenIntent;
|
||||||
|
Bitmap iconBitmap = getBitmapFromDrawable(
|
||||||
|
getPkgIcon(enr.getEntry().getSbn().getPackageName()));
|
||||||
|
|
||||||
|
final ImageView snapshot = new ImageView(mContext);
|
||||||
|
snapshot.setImageBitmap(iconBitmap);
|
||||||
|
snapshot.layout(0, 0, mIconSize, mIconSize);
|
||||||
|
|
||||||
|
ClipDescription clipDescription = new ClipDescription("Drag And Drop",
|
||||||
|
new String[]{ClipDescription.MIMETYPE_APPLICATION_ACTIVITY});
|
||||||
|
Intent dragIntent = new Intent();
|
||||||
|
dragIntent.putExtra("android.intent.extra.PENDING_INTENT", contentIntent);
|
||||||
|
dragIntent.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
|
||||||
|
ClipData.Item item = new ClipData.Item(dragIntent);
|
||||||
|
ClipData dragData = new ClipData(clipDescription, item);
|
||||||
|
View.DragShadowBuilder myShadow = new View.DragShadowBuilder(snapshot);
|
||||||
|
view.setOnDragListener(getDraggedViewDragListener());
|
||||||
|
view.startDragAndDrop(dragData, myShadow, null, View.DRAG_FLAG_GLOBAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Drawable getPkgIcon(String pkgName) {
|
||||||
|
Drawable pkgicon = null;
|
||||||
|
PackageManager pm = mContext.getPackageManager();
|
||||||
|
ApplicationInfo info;
|
||||||
|
try {
|
||||||
|
info = pm.getApplicationInfo(
|
||||||
|
pkgName,
|
||||||
|
PackageManager.MATCH_UNINSTALLED_PACKAGES
|
||||||
|
| PackageManager.MATCH_DISABLED_COMPONENTS
|
||||||
|
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
|
||||||
|
| PackageManager.MATCH_DIRECT_BOOT_AWARE);
|
||||||
|
if (info != null) {
|
||||||
|
pkgicon = pm.getApplicationIcon(info);
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, " application info is null ");
|
||||||
|
pkgicon = pm.getDefaultActivityIcon();
|
||||||
|
}
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
Log.d(TAG, "can not find package with : " + pkgName);
|
||||||
|
pkgicon = pm.getDefaultActivityIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgicon;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap getBitmapFromDrawable(@NonNull Drawable drawable) {
|
||||||
|
final Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
|
||||||
|
drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||||
|
final Canvas canvas = new Canvas(bmp);
|
||||||
|
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||||
|
drawable.draw(canvas);
|
||||||
|
return bmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View.OnDragListener getDraggedViewDragListener() {
|
||||||
|
return (view, dragEvent) -> {
|
||||||
|
switch (dragEvent.getAction()) {
|
||||||
|
case DragEvent.ACTION_DRAG_STARTED:
|
||||||
|
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||||
|
if (view instanceof ExpandableNotificationRow) {
|
||||||
|
ExpandableNotificationRow enr = (ExpandableNotificationRow) view;
|
||||||
|
if (enr.isPinned()) {
|
||||||
|
mHeadsUpManager.releaseAllImmediately();
|
||||||
|
} else {
|
||||||
|
Dependency.get(ShadeController.class).animateCollapsePanels(
|
||||||
|
CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case DragEvent.ACTION_DRAG_ENDED:
|
||||||
|
if (dragEvent.getResult()) {
|
||||||
|
if (view instanceof ExpandableNotificationRow) {
|
||||||
|
ExpandableNotificationRow enr = (ExpandableNotificationRow) view;
|
||||||
|
enr.dragAndDropSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -183,6 +183,8 @@ public class NotificationStackScrollLayoutController {
|
|||||||
private int mBarState;
|
private int mBarState;
|
||||||
private HeadsUpAppearanceController mHeadsUpAppearanceController;
|
private HeadsUpAppearanceController mHeadsUpAppearanceController;
|
||||||
|
|
||||||
|
private View mLongPressedView;
|
||||||
|
|
||||||
private final NotificationListContainerImpl mNotificationListContainer =
|
private final NotificationListContainerImpl mNotificationListContainer =
|
||||||
new NotificationListContainerImpl();
|
new NotificationListContainerImpl();
|
||||||
|
|
||||||
@@ -491,6 +493,11 @@ public class NotificationStackScrollLayoutController {
|
|||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLongPressSent(View v) {
|
||||||
|
mLongPressedView = v;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBeginDrag(View v) {
|
public void onBeginDrag(View v) {
|
||||||
mFalsingCollector.onNotificationStartDismissing();
|
mFalsingCollector.onNotificationStartDismissing();
|
||||||
@@ -1435,6 +1442,10 @@ public class NotificationStackScrollLayoutController {
|
|||||||
return mDynamicPrivacyController.isInLockedDownShade();
|
return mDynamicPrivacyController.isInLockedDownShade();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLongPressInProgress() {
|
||||||
|
return mLongPressedView != null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the dimmed state for all of the notification views.
|
* Set the dimmed state for all of the notification views.
|
||||||
*/
|
*/
|
||||||
@@ -1689,17 +1700,23 @@ public class NotificationStackScrollLayoutController {
|
|||||||
mView.handleEmptySpaceClick(ev);
|
mView.handleEmptySpaceClick(ev);
|
||||||
|
|
||||||
NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
|
NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
|
||||||
|
|
||||||
|
boolean longPressWantsIt = false;
|
||||||
|
if (mLongPressedView != null) {
|
||||||
|
longPressWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
|
||||||
|
}
|
||||||
boolean expandWantsIt = false;
|
boolean expandWantsIt = false;
|
||||||
if (!mSwipeHelper.isSwiping()
|
if (mLongPressedView == null && !mSwipeHelper.isSwiping()
|
||||||
&& !mView.getOnlyScrollingInThisMotion() && guts == null) {
|
&& !mView.getOnlyScrollingInThisMotion() && guts == null) {
|
||||||
expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev);
|
expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev);
|
||||||
}
|
}
|
||||||
boolean scrollWantsIt = false;
|
boolean scrollWantsIt = false;
|
||||||
if (!mSwipeHelper.isSwiping() && !mView.isExpandingNotification()) {
|
if (mLongPressedView == null && !mSwipeHelper.isSwiping()
|
||||||
|
&& !mView.isExpandingNotification()) {
|
||||||
scrollWantsIt = mView.onInterceptTouchEventScroll(ev);
|
scrollWantsIt = mView.onInterceptTouchEventScroll(ev);
|
||||||
}
|
}
|
||||||
boolean swipeWantsIt = false;
|
boolean swipeWantsIt = false;
|
||||||
if (!mView.isBeingDragged()
|
if (mLongPressedView == null && !mView.isBeingDragged()
|
||||||
&& !mView.isExpandingNotification()
|
&& !mView.isExpandingNotification()
|
||||||
&& !mView.getExpandedInThisMotion()
|
&& !mView.getExpandedInThisMotion()
|
||||||
&& !mView.getOnlyScrollingInThisMotion()
|
&& !mView.getOnlyScrollingInThisMotion()
|
||||||
@@ -1727,7 +1744,7 @@ public class NotificationStackScrollLayoutController {
|
|||||||
InteractionJankMonitor.getInstance().begin(mView,
|
InteractionJankMonitor.getInstance().begin(mView,
|
||||||
CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
|
CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
|
||||||
}
|
}
|
||||||
return swipeWantsIt || scrollWantsIt || expandWantsIt;
|
return swipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1736,11 +1753,15 @@ public class NotificationStackScrollLayoutController {
|
|||||||
boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
|
boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
|
||||||
|| ev.getActionMasked() == MotionEvent.ACTION_UP;
|
|| ev.getActionMasked() == MotionEvent.ACTION_UP;
|
||||||
mView.handleEmptySpaceClick(ev);
|
mView.handleEmptySpaceClick(ev);
|
||||||
|
boolean longPressWantsIt = false;
|
||||||
|
if (guts != null && mLongPressedView != null) {
|
||||||
|
longPressWantsIt = mSwipeHelper.onTouchEvent(ev);
|
||||||
|
}
|
||||||
boolean expandWantsIt = false;
|
boolean expandWantsIt = false;
|
||||||
boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion();
|
boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion();
|
||||||
boolean expandingNotification = mView.isExpandingNotification();
|
boolean expandingNotification = mView.isExpandingNotification();
|
||||||
if (mView.getIsExpanded() && !mSwipeHelper.isSwiping() && !onlyScrollingInThisMotion
|
if (mLongPressedView == null && mView.getIsExpanded()
|
||||||
&& guts == null) {
|
&& !mSwipeHelper.isSwiping() && !onlyScrollingInThisMotion && guts == null) {
|
||||||
ExpandHelper expandHelper = mView.getExpandHelper();
|
ExpandHelper expandHelper = mView.getExpandHelper();
|
||||||
if (isCancelOrUp) {
|
if (isCancelOrUp) {
|
||||||
expandHelper.onlyObserveMovements(false);
|
expandHelper.onlyObserveMovements(false);
|
||||||
@@ -1754,12 +1775,12 @@ public class NotificationStackScrollLayoutController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean scrollerWantsIt = false;
|
boolean scrollerWantsIt = false;
|
||||||
if (mView.isExpanded() && !mSwipeHelper.isSwiping() && !expandingNotification
|
if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping()
|
||||||
&& !mView.getDisallowScrollingInThisMotion()) {
|
&& !expandingNotification && !mView.getDisallowScrollingInThisMotion()) {
|
||||||
scrollerWantsIt = mView.onScrollTouch(ev);
|
scrollerWantsIt = mView.onScrollTouch(ev);
|
||||||
}
|
}
|
||||||
boolean horizontalSwipeWantsIt = false;
|
boolean horizontalSwipeWantsIt = false;
|
||||||
if (!mView.isBeingDragged()
|
if (mLongPressedView == null && !mView.isBeingDragged()
|
||||||
&& !expandingNotification
|
&& !expandingNotification
|
||||||
&& !mView.getExpandedInThisMotion()
|
&& !mView.getExpandedInThisMotion()
|
||||||
&& !onlyScrollingInThisMotion
|
&& !onlyScrollingInThisMotion
|
||||||
@@ -1785,7 +1806,7 @@ public class NotificationStackScrollLayoutController {
|
|||||||
mView.setCheckForLeaveBehind(true);
|
mView.setCheckForLeaveBehind(true);
|
||||||
}
|
}
|
||||||
traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
|
traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
|
||||||
return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt;
|
return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || longPressWantsIt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
|
private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
|
||||||
|
|||||||
@@ -433,6 +433,7 @@ public class StackStateAnimator {
|
|||||||
if (row.isDismissed()) {
|
if (row.isDismissed()) {
|
||||||
needsAnimation = false;
|
needsAnimation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationEntry entry = row.getEntry();
|
NotificationEntry entry = row.getEntry();
|
||||||
StatusBarIconView icon = entry.getIcons().getStatusBarIcon();
|
StatusBarIconView icon = entry.getIcons().getStatusBarIcon();
|
||||||
final StatusBarIconView centeredIcon = entry.getIcons().getCenteredIcon();
|
final StatusBarIconView centeredIcon = entry.getIcons().getCenteredIcon();
|
||||||
@@ -442,7 +443,8 @@ public class StackStateAnimator {
|
|||||||
if (icon.getParent() != null) {
|
if (icon.getParent() != null) {
|
||||||
icon.getLocationOnScreen(mTmpLocation);
|
icon.getLocationOnScreen(mTmpLocation);
|
||||||
float iconPosition = mTmpLocation[0] - icon.getTranslationX()
|
float iconPosition = mTmpLocation[0] - icon.getTranslationX()
|
||||||
+ ViewState.getFinalTranslationX(icon) + icon.getWidth() * 0.25f;
|
+ ViewState.getFinalTranslationX(icon)
|
||||||
|
+ icon.getWidth() * 0.25f;
|
||||||
mHostLayout.getLocationOnScreen(mTmpLocation);
|
mHostLayout.getLocationOnScreen(mTmpLocation);
|
||||||
targetLocation = iconPosition - mTmpLocation[0];
|
targetLocation = iconPosition - mTmpLocation[0];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3947,7 +3947,9 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
if (mStatusBar.isBouncerShowing()) {
|
if (mStatusBar.isBouncerShowing()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
|
if (mBar.panelEnabled()
|
||||||
|
&& !mNotificationStackScrollLayoutController.isLongPressInProgress()
|
||||||
|
&& mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
|
||||||
mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
|
mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
|
||||||
mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
|
mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
|
||||||
return true;
|
return true;
|
||||||
@@ -3999,6 +4001,7 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
|
if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
|
||||||
|
&& !mNotificationStackScrollLayoutController.isLongPressInProgress()
|
||||||
&& mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
|
&& mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
|
||||||
mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
|
mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember
|
|||||||
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
|
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
|
||||||
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
|
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||||
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
|
||||||
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
|
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
|
||||||
import com.android.systemui.statusbar.policy.HeadsUpUtil;
|
import com.android.systemui.statusbar.policy.HeadsUpUtil;
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||||
@@ -402,6 +403,53 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
mIsCollapsingToShowActivityOverLockscreen = false;
|
mIsCollapsingToShowActivityOverLockscreen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a notification is dropped on proper target window.
|
||||||
|
* Intent that is included in this entry notification,
|
||||||
|
* will be sent by {@link ExpandableNotificationRowDragController}
|
||||||
|
*
|
||||||
|
* @param entry notification entry that is dropped.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onDragSuccess(NotificationEntry entry) {
|
||||||
|
// this method is not responsible for intent sending.
|
||||||
|
// will focus follow operation only after drag-and-drop that notification.
|
||||||
|
NotificationVisibility.NotificationLocation location =
|
||||||
|
NotificationLogger.getNotificationLocation(entry);
|
||||||
|
final NotificationVisibility nv = NotificationVisibility.obtain(entry.getKey(),
|
||||||
|
entry.getRanking().getRank(), getVisibleNotificationsCount(), true, location);
|
||||||
|
|
||||||
|
// retrieve the group summary to remove with this entry before we tell NMS the
|
||||||
|
// notification was clicked to avoid a race condition
|
||||||
|
final boolean shouldAutoCancel = shouldAutoCancel(entry.getSbn());
|
||||||
|
final NotificationEntry summaryToRemove = shouldAutoCancel
|
||||||
|
? mOnUserInteractionCallback.getGroupSummaryToDismiss(entry) : null;
|
||||||
|
|
||||||
|
String notificationKey = entry.getKey();
|
||||||
|
// inform NMS that the notification was clicked
|
||||||
|
mClickNotifier.onNotificationClick(notificationKey, nv);
|
||||||
|
|
||||||
|
if (shouldAutoCancel || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
|
||||||
|
notificationKey)) {
|
||||||
|
// Immediately remove notification from visually showing.
|
||||||
|
// We have to post the removal to the UI thread for synchronization.
|
||||||
|
mMainThreadHandler.post(() -> {
|
||||||
|
final Runnable removeNotification = () ->
|
||||||
|
mOnUserInteractionCallback.onDismiss(
|
||||||
|
entry, REASON_CLICK, summaryToRemove);
|
||||||
|
if (mPresenter.isCollapsing()) {
|
||||||
|
// To avoid lags we're only performing the remove
|
||||||
|
// after the shade is collapsed
|
||||||
|
mShadeController.addPostCollapseAction(removeNotification);
|
||||||
|
} else {
|
||||||
|
removeNotification.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsCollapsingToShowActivityOverLockscreen = false;
|
||||||
|
}
|
||||||
|
|
||||||
private void expandBubbleStackOnMainThread(NotificationEntry entry) {
|
private void expandBubbleStackOnMainThread(NotificationEntry entry) {
|
||||||
if (!mBubblesManagerOptional.isPresent()) {
|
if (!mBubblesManagerOptional.isPresent()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* 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.notification.row;
|
||||||
|
|
||||||
|
import static android.view.DragEvent.ACTION_DRAG_STARTED;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.testing.AndroidTestingRunner;
|
||||||
|
import android.testing.TestableLooper;
|
||||||
|
import android.testing.TestableLooper.RunWithLooper;
|
||||||
|
import android.view.DragEvent;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||||
|
import com.android.systemui.statusbar.phone.ShadeController;
|
||||||
|
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner.class)
|
||||||
|
@RunWithLooper
|
||||||
|
public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase {
|
||||||
|
|
||||||
|
private ExpandableNotificationRow mRow;
|
||||||
|
private ExpandableNotificationRow mGroupRow;
|
||||||
|
private ExpandableNotificationRowDragController mController;
|
||||||
|
private NotificationTestHelper mNotificationTestHelper;
|
||||||
|
|
||||||
|
private NotificationGutsManager mGutsManager = mock(NotificationGutsManager.class);
|
||||||
|
private HeadsUpManager mHeadsUpManager = mock(HeadsUpManager.class);
|
||||||
|
private NotificationMenuRow mMenuRow = mock(NotificationMenuRow.class);
|
||||||
|
private NotificationMenuRowPlugin.MenuItem mMenuItem =
|
||||||
|
mock(NotificationMenuRowPlugin.MenuItem.class);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
allowTestableLooperAsMainThread();
|
||||||
|
|
||||||
|
mDependency.injectMockDependency(ShadeController.class);
|
||||||
|
|
||||||
|
mNotificationTestHelper = new NotificationTestHelper(
|
||||||
|
mContext,
|
||||||
|
mDependency,
|
||||||
|
TestableLooper.get(this));
|
||||||
|
mRow = mNotificationTestHelper.createRow();
|
||||||
|
mGroupRow = mNotificationTestHelper.createGroup(4);
|
||||||
|
when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
|
||||||
|
|
||||||
|
mController = new ExpandableNotificationRowDragController(mContext, mHeadsUpManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoStartDragHeadsUpNotif_startDragAndDrop() throws Exception {
|
||||||
|
ExpandableNotificationRowDragController controller = createSpyController();
|
||||||
|
mRow.setDragController(controller);
|
||||||
|
mRow.setHeadsUp(true);
|
||||||
|
mRow.setPinned(true);
|
||||||
|
|
||||||
|
mRow.doLongClickCallback(0, 0);
|
||||||
|
mRow.doDragCallback(0, 0);
|
||||||
|
verify(controller).startDragAndDrop(mRow);
|
||||||
|
|
||||||
|
// Simulate the drag start
|
||||||
|
mRow.dispatchDragEvent(DragEvent.obtain(ACTION_DRAG_STARTED, 0, 0, 0, 0, null, null, null,
|
||||||
|
null, null, false));
|
||||||
|
verify(mHeadsUpManager, times(1)).releaseAllImmediately();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoStartDragNotif() throws Exception {
|
||||||
|
ExpandableNotificationRowDragController controller = createSpyController();
|
||||||
|
mRow.setDragController(controller);
|
||||||
|
|
||||||
|
mDependency.get(ShadeController.class).instantExpandNotificationsPanel();
|
||||||
|
mRow.doDragCallback(0, 0);
|
||||||
|
verify(controller).startDragAndDrop(mRow);
|
||||||
|
|
||||||
|
// Simulate the drag start
|
||||||
|
mRow.dispatchDragEvent(DragEvent.obtain(ACTION_DRAG_STARTED, 0, 0, 0, 0, null, null, null,
|
||||||
|
null, null, false));
|
||||||
|
verify(mDependency.get(ShadeController.class)).animateCollapsePanels(0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpandableNotificationRowDragController createSpyController() {
|
||||||
|
return spy(mController);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -265,7 +265,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
|
|||||||
new FalsingManagerFake(),
|
new FalsingManagerFake(),
|
||||||
new FalsingCollectorFake(),
|
new FalsingCollectorFake(),
|
||||||
mPeopleNotificationIdentifier,
|
mPeopleNotificationIdentifier,
|
||||||
Optional.of(mock(BubblesManager.class))
|
Optional.of(mock(BubblesManager.class)),
|
||||||
|
mock(ExpandableNotificationRowDragController.class)
|
||||||
));
|
));
|
||||||
|
|
||||||
when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
|
when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
|
||||||
|
|||||||
Reference in New Issue
Block a user