Merge "Couple fixes for drag and drop" into tm-dev
This commit is contained in:
@@ -35,6 +35,9 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA
|
|||||||
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
||||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
import android.content.ClipDescription;
|
import android.content.ClipDescription;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
@@ -54,6 +57,7 @@ import com.android.internal.logging.UiEventLogger;
|
|||||||
import com.android.internal.protolog.common.ProtoLog;
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
import com.android.launcher3.icons.IconProvider;
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.R;
|
import com.android.wm.shell.R;
|
||||||
|
import com.android.wm.shell.animation.Interpolators;
|
||||||
import com.android.wm.shell.common.DisplayController;
|
import com.android.wm.shell.common.DisplayController;
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||||
@@ -205,6 +209,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
|
|||||||
break;
|
break;
|
||||||
case ACTION_DRAG_ENTERED:
|
case ACTION_DRAG_ENTERED:
|
||||||
pd.dragLayout.show();
|
pd.dragLayout.show();
|
||||||
|
pd.dragLayout.update(event);
|
||||||
break;
|
break;
|
||||||
case ACTION_DRAG_LOCATION:
|
case ACTION_DRAG_LOCATION:
|
||||||
pd.dragLayout.update(event);
|
pd.dragLayout.update(event);
|
||||||
@@ -250,10 +255,6 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
|
|||||||
// Hide the window if another drag hasn't been started while animating the drop
|
// Hide the window if another drag hasn't been started while animating the drop
|
||||||
setDropTargetWindowVisibility(pd, View.INVISIBLE);
|
setDropTargetWindowVisibility(pd, View.INVISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the drag surface
|
|
||||||
mTransaction.reparent(dragSurface, null);
|
|
||||||
mTransaction.apply();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,6 +122,13 @@ public class DragAndDropPolicy {
|
|||||||
return mSession.runningTaskInfo;
|
return mSession.runningTaskInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of targets.
|
||||||
|
*/
|
||||||
|
int getNumTargets() {
|
||||||
|
return mTargets.size();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the target's regions based on the current state of the device and display.
|
* Returns the target's regions based on the current state of the device and display.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.wm.shell.draganddrop;
|
package com.android.wm.shell.draganddrop;
|
||||||
|
|
||||||
import static android.app.StatusBarManager.DISABLE_NONE;
|
import static android.app.StatusBarManager.DISABLE_NONE;
|
||||||
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
|
||||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||||
@@ -24,6 +25,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
|
|||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.StatusBarManager;
|
import android.app.StatusBarManager;
|
||||||
@@ -44,6 +46,7 @@ import com.android.internal.logging.InstanceId;
|
|||||||
import com.android.internal.protolog.common.ProtoLog;
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
import com.android.launcher3.icons.IconProvider;
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.R;
|
import com.android.wm.shell.R;
|
||||||
|
import com.android.wm.shell.animation.Interpolators;
|
||||||
import com.android.wm.shell.common.DisplayLayout;
|
import com.android.wm.shell.common.DisplayLayout;
|
||||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||||
@@ -135,6 +138,12 @@ public class DragLayout extends LinearLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateContainerMarginsForSingleTask() {
|
||||||
|
mDropZoneView1.setContainerMargin(
|
||||||
|
mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin);
|
||||||
|
mDropZoneView2.setContainerMargin(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateContainerMargins(int orientation) {
|
private void updateContainerMargins(int orientation) {
|
||||||
final float halfMargin = mDisplayMargin / 2f;
|
final float halfMargin = mDisplayMargin / 2f;
|
||||||
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||||
@@ -165,11 +174,20 @@ public class DragLayout extends LinearLayout {
|
|||||||
if (!alreadyInSplit) {
|
if (!alreadyInSplit) {
|
||||||
ActivityManager.RunningTaskInfo taskInfo1 = mPolicy.getLatestRunningTask();
|
ActivityManager.RunningTaskInfo taskInfo1 = mPolicy.getLatestRunningTask();
|
||||||
if (taskInfo1 != null) {
|
if (taskInfo1 != null) {
|
||||||
Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
|
final int activityType = taskInfo1.getActivityType();
|
||||||
int bgColor1 = getResizingBackgroundColor(taskInfo1);
|
if (activityType == ACTIVITY_TYPE_STANDARD) {
|
||||||
mDropZoneView1.setAppInfo(bgColor1, icon1);
|
Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
|
||||||
mDropZoneView2.setAppInfo(bgColor1, icon1);
|
int bgColor1 = getResizingBackgroundColor(taskInfo1);
|
||||||
updateDropZoneSizes(null, null); // passing null splits the views evenly
|
mDropZoneView1.setAppInfo(bgColor1, icon1);
|
||||||
|
mDropZoneView2.setAppInfo(bgColor1, icon1);
|
||||||
|
updateDropZoneSizes(null, null); // passing null splits the views evenly
|
||||||
|
} else {
|
||||||
|
// We use the first drop zone to show the fullscreen highlight, and don't need
|
||||||
|
// to set additional info
|
||||||
|
mDropZoneView1.setForceIgnoreBottomMargin(true);
|
||||||
|
updateDropZoneSizesForSingleTask();
|
||||||
|
updateContainerMarginsForSingleTask();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We're already in split so get taskInfo from the controller to populate icon / color.
|
// We're already in split so get taskInfo from the controller to populate icon / color.
|
||||||
@@ -195,6 +213,21 @@ public class DragLayout extends LinearLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateDropZoneSizesForSingleTask() {
|
||||||
|
final LinearLayout.LayoutParams dropZoneView1 =
|
||||||
|
(LayoutParams) mDropZoneView1.getLayoutParams();
|
||||||
|
final LinearLayout.LayoutParams dropZoneView2 =
|
||||||
|
(LayoutParams) mDropZoneView2.getLayoutParams();
|
||||||
|
dropZoneView1.width = MATCH_PARENT;
|
||||||
|
dropZoneView1.height = MATCH_PARENT;
|
||||||
|
dropZoneView2.width = 0;
|
||||||
|
dropZoneView2.height = 0;
|
||||||
|
dropZoneView1.weight = 1;
|
||||||
|
dropZoneView2.weight = 0;
|
||||||
|
mDropZoneView1.setLayoutParams(dropZoneView1);
|
||||||
|
mDropZoneView2.setLayoutParams(dropZoneView2);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the size of the two drop zones based on the provided bounds. The divider sits between
|
* Sets the size of the two drop zones based on the provided bounds. The divider sits between
|
||||||
* the views and its size is included in the calculations.
|
* the views and its size is included in the calculations.
|
||||||
@@ -265,9 +298,12 @@ public class DragLayout extends LinearLayout {
|
|||||||
// Animating to no target
|
// Animating to no target
|
||||||
animateSplitContainers(false, null /* animCompleteCallback */);
|
animateSplitContainers(false, null /* animCompleteCallback */);
|
||||||
} else if (mCurrentTarget == null) {
|
} else if (mCurrentTarget == null) {
|
||||||
// Animating to first target
|
if (mPolicy.getNumTargets() == 1) {
|
||||||
animateSplitContainers(true, null /* animCompleteCallback */);
|
animateFullscreenContainer(true);
|
||||||
animateHighlight(target);
|
} else {
|
||||||
|
animateSplitContainers(true, null /* animCompleteCallback */);
|
||||||
|
animateHighlight(target);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Switching between targets
|
// Switching between targets
|
||||||
mDropZoneView1.animateSwitch();
|
mDropZoneView1.animateSwitch();
|
||||||
@@ -283,6 +319,10 @@ public class DragLayout extends LinearLayout {
|
|||||||
public void hide(DragEvent event, Runnable hideCompleteCallback) {
|
public void hide(DragEvent event, Runnable hideCompleteCallback) {
|
||||||
mIsShowing = false;
|
mIsShowing = false;
|
||||||
animateSplitContainers(false, hideCompleteCallback);
|
animateSplitContainers(false, hideCompleteCallback);
|
||||||
|
// Reset the state if we previously force-ignore the bottom margin
|
||||||
|
mDropZoneView1.setForceIgnoreBottomMargin(false);
|
||||||
|
mDropZoneView2.setForceIgnoreBottomMargin(false);
|
||||||
|
updateContainerMargins(getResources().getConfiguration().orientation);
|
||||||
mCurrentTarget = null;
|
mCurrentTarget = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,11 +337,63 @@ public class DragLayout extends LinearLayout {
|
|||||||
// Process the drop
|
// Process the drop
|
||||||
mPolicy.handleDrop(mCurrentTarget, event.getClipData());
|
mPolicy.handleDrop(mCurrentTarget, event.getClipData());
|
||||||
|
|
||||||
// TODO(b/169894807): Coordinate with dragSurface
|
// Start animating the drop UI out with the drag surface
|
||||||
hide(event, dropCompleteCallback);
|
hide(event, dropCompleteCallback);
|
||||||
|
hideDragSurface(dragSurface);
|
||||||
return handledDrop;
|
return handledDrop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void hideDragSurface(SurfaceControl dragSurface) {
|
||||||
|
final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
|
||||||
|
final ValueAnimator dragSurfaceAnimator = ValueAnimator.ofFloat(0f, 1f);
|
||||||
|
// Currently the splash icon animation runs with the default ValueAnimator duration of
|
||||||
|
// 300ms
|
||||||
|
dragSurfaceAnimator.setDuration(300);
|
||||||
|
dragSurfaceAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||||
|
dragSurfaceAnimator.addUpdateListener(animation -> {
|
||||||
|
float t = animation.getAnimatedFraction();
|
||||||
|
float alpha = 1f - t;
|
||||||
|
// TODO: Scale the drag surface as well once we make all the source surfaces
|
||||||
|
// consistent
|
||||||
|
tx.setAlpha(dragSurface, alpha);
|
||||||
|
tx.apply();
|
||||||
|
});
|
||||||
|
dragSurfaceAnimator.addListener(new AnimatorListenerAdapter() {
|
||||||
|
private boolean mCanceled = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationCancel(Animator animation) {
|
||||||
|
cleanUpSurface();
|
||||||
|
mCanceled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
if (mCanceled) {
|
||||||
|
// Already handled above
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cleanUpSurface();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanUpSurface() {
|
||||||
|
// Clean up the drag surface
|
||||||
|
tx.remove(dragSurface);
|
||||||
|
tx.apply();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dragSurfaceAnimator.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animateFullscreenContainer(boolean visible) {
|
||||||
|
mStatusBarManager.disable(visible
|
||||||
|
? HIDE_STATUS_BAR_FLAGS
|
||||||
|
: DISABLE_NONE);
|
||||||
|
// We're only using the first drop zone if there is one fullscreen target
|
||||||
|
mDropZoneView1.setShowingMargin(visible);
|
||||||
|
mDropZoneView1.setShowingHighlight(visible);
|
||||||
|
}
|
||||||
|
|
||||||
private void animateSplitContainers(boolean visible, Runnable animCompleteCallback) {
|
private void animateSplitContainers(boolean visible, Runnable animCompleteCallback) {
|
||||||
mStatusBarManager.disable(visible
|
mStatusBarManager.disable(visible
|
||||||
? HIDE_STATUS_BAR_FLAGS
|
? HIDE_STATUS_BAR_FLAGS
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public class DropZoneView extends FrameLayout {
|
|||||||
private final float[] mContainerMargin = new float[4];
|
private final float[] mContainerMargin = new float[4];
|
||||||
private float mCornerRadius;
|
private float mCornerRadius;
|
||||||
private float mBottomInset;
|
private float mBottomInset;
|
||||||
|
private boolean mIgnoreBottomMargin;
|
||||||
private int mMarginColor; // i.e. color used for negative space like the container insets
|
private int mMarginColor; // i.e. color used for negative space like the container insets
|
||||||
|
|
||||||
private boolean mShowingHighlight;
|
private boolean mShowingHighlight;
|
||||||
@@ -141,6 +142,14 @@ public class DropZoneView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Ignores the bottom margin provided by the insets. */
|
||||||
|
public void setForceIgnoreBottomMargin(boolean ignoreBottomMargin) {
|
||||||
|
mIgnoreBottomMargin = ignoreBottomMargin;
|
||||||
|
if (mMarginPercent > 0) {
|
||||||
|
mMarginView.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Sets the bottom inset so the drop zones are above bottom navigation. */
|
/** Sets the bottom inset so the drop zones are above bottom navigation. */
|
||||||
public void setBottomInset(float bottom) {
|
public void setBottomInset(float bottom) {
|
||||||
mBottomInset = bottom;
|
mBottomInset = bottom;
|
||||||
@@ -257,7 +266,8 @@ public class DropZoneView extends FrameLayout {
|
|||||||
mPath.addRoundRect(mContainerMargin[0] * mMarginPercent,
|
mPath.addRoundRect(mContainerMargin[0] * mMarginPercent,
|
||||||
mContainerMargin[1] * mMarginPercent,
|
mContainerMargin[1] * mMarginPercent,
|
||||||
getWidth() - (mContainerMargin[2] * mMarginPercent),
|
getWidth() - (mContainerMargin[2] * mMarginPercent),
|
||||||
getHeight() - (mContainerMargin[3] * mMarginPercent) - mBottomInset,
|
getHeight() - (mContainerMargin[3] * mMarginPercent)
|
||||||
|
- (mIgnoreBottomMargin ? 0 : mBottomInset),
|
||||||
mCornerRadius * mMarginPercent,
|
mCornerRadius * mMarginPercent,
|
||||||
mCornerRadius * mMarginPercent,
|
mCornerRadius * mMarginPercent,
|
||||||
Path.Direction.CW);
|
Path.Direction.CW);
|
||||||
|
|||||||
@@ -2407,4 +2407,7 @@
|
|||||||
<string name="add_user_supervised" translatable="false">@*android:string/supervised_user_creation_label</string>
|
<string name="add_user_supervised" translatable="false">@*android:string/supervised_user_creation_label</string>
|
||||||
<!-- Manage users - For system user management [CHAR LIMIT=40] -->
|
<!-- Manage users - For system user management [CHAR LIMIT=40] -->
|
||||||
<string name="manage_users">Manage users</string>
|
<string name="manage_users">Manage users</string>
|
||||||
|
|
||||||
|
<!-- Toast shown when a notification does not support dragging to split [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="drag_split_not_supported">This notification does not support dragging to Splitscreen.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -17,6 +17,11 @@
|
|||||||
|
|
||||||
package com.android.systemui.statusbar.notification.row;
|
package com.android.systemui.statusbar.notification.row;
|
||||||
|
|
||||||
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
@@ -33,13 +38,15 @@ import android.service.notification.StatusBarNotification;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.DragEvent;
|
import android.view.DragEvent;
|
||||||
import android.view.HapticFeedbackConstants;
|
import android.view.HapticFeedbackConstants;
|
||||||
|
import android.view.SurfaceControl;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.android.systemui.Dependency;
|
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.animation.Interpolators;
|
||||||
import com.android.systemui.statusbar.CommandQueue;
|
import com.android.systemui.statusbar.CommandQueue;
|
||||||
import com.android.systemui.statusbar.phone.ShadeController;
|
import com.android.systemui.statusbar.phone.ShadeController;
|
||||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||||
@@ -55,12 +62,15 @@ public class ExpandableNotificationRowDragController {
|
|||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final HeadsUpManager mHeadsUpManager;
|
private final HeadsUpManager mHeadsUpManager;
|
||||||
|
private final ShadeController mShadeController;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ExpandableNotificationRowDragController(Context context,
|
public ExpandableNotificationRowDragController(Context context,
|
||||||
HeadsUpManager headsUpManager) {
|
HeadsUpManager headsUpManager,
|
||||||
|
ShadeController shadeController) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mHeadsUpManager = headsUpManager;
|
mHeadsUpManager = headsUpManager;
|
||||||
|
mShadeController = shadeController;
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
@@ -87,6 +97,16 @@ public class ExpandableNotificationRowDragController {
|
|||||||
final PendingIntent contentIntent = notification.contentIntent != null
|
final PendingIntent contentIntent = notification.contentIntent != null
|
||||||
? notification.contentIntent
|
? notification.contentIntent
|
||||||
: notification.fullScreenIntent;
|
: notification.fullScreenIntent;
|
||||||
|
if (contentIntent == null) {
|
||||||
|
if (!enr.isPinned()) {
|
||||||
|
// We dismiss the shade for consistency, but also because toasts currently don't
|
||||||
|
// show above the shade
|
||||||
|
dismissShade();
|
||||||
|
}
|
||||||
|
Toast.makeText(mContext, R.string.drag_split_not_supported, LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
Bitmap iconBitmap = getBitmapFromDrawable(
|
Bitmap iconBitmap = getBitmapFromDrawable(
|
||||||
getPkgIcon(enr.getEntry().getSbn().getPackageName()));
|
getPkgIcon(enr.getEntry().getSbn().getPackageName()));
|
||||||
|
|
||||||
@@ -97,15 +117,30 @@ public class ExpandableNotificationRowDragController {
|
|||||||
ClipDescription clipDescription = new ClipDescription("Drag And Drop",
|
ClipDescription clipDescription = new ClipDescription("Drag And Drop",
|
||||||
new String[]{ClipDescription.MIMETYPE_APPLICATION_ACTIVITY});
|
new String[]{ClipDescription.MIMETYPE_APPLICATION_ACTIVITY});
|
||||||
Intent dragIntent = new Intent();
|
Intent dragIntent = new Intent();
|
||||||
dragIntent.putExtra("android.intent.extra.PENDING_INTENT", contentIntent);
|
dragIntent.putExtra(ClipDescription.EXTRA_PENDING_INTENT, contentIntent);
|
||||||
dragIntent.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
|
dragIntent.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
|
||||||
ClipData.Item item = new ClipData.Item(dragIntent);
|
ClipData.Item item = new ClipData.Item(dragIntent);
|
||||||
ClipData dragData = new ClipData(clipDescription, item);
|
ClipData dragData = new ClipData(clipDescription, item);
|
||||||
View.DragShadowBuilder myShadow = new View.DragShadowBuilder(snapshot);
|
View.DragShadowBuilder myShadow = new View.DragShadowBuilder(snapshot);
|
||||||
view.setOnDragListener(getDraggedViewDragListener());
|
view.setOnDragListener(getDraggedViewDragListener());
|
||||||
view.startDragAndDrop(dragData, myShadow, null, View.DRAG_FLAG_GLOBAL);
|
boolean result = view.startDragAndDrop(dragData, myShadow, null, View.DRAG_FLAG_GLOBAL
|
||||||
|
| View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION);
|
||||||
|
if (result) {
|
||||||
|
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||||
|
if (enr.isPinned()) {
|
||||||
|
mHeadsUpManager.releaseAllImmediately();
|
||||||
|
} else {
|
||||||
|
dismissShade();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void dismissShade() {
|
||||||
|
// Speed up dismissing the shade since the drag needs to be handled by
|
||||||
|
// the shell layer underneath
|
||||||
|
mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
|
||||||
|
false /* delayed */, 1.1f /* speedUpFactor */);
|
||||||
|
}
|
||||||
|
|
||||||
private Drawable getPkgIcon(String pkgName) {
|
private Drawable getPkgIcon(String pkgName) {
|
||||||
Drawable pkgicon = null;
|
Drawable pkgicon = null;
|
||||||
@@ -145,16 +180,6 @@ public class ExpandableNotificationRowDragController {
|
|||||||
return (view, dragEvent) -> {
|
return (view, dragEvent) -> {
|
||||||
switch (dragEvent.getAction()) {
|
switch (dragEvent.getAction()) {
|
||||||
case DragEvent.ACTION_DRAG_STARTED:
|
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;
|
return true;
|
||||||
case DragEvent.ACTION_DRAG_ENDED:
|
case DragEvent.ACTION_DRAG_ENDED:
|
||||||
if (dragEvent.getResult()) {
|
if (dragEvent.getResult()) {
|
||||||
@@ -162,10 +187,55 @@ public class ExpandableNotificationRowDragController {
|
|||||||
ExpandableNotificationRow enr = (ExpandableNotificationRow) view;
|
ExpandableNotificationRow enr = (ExpandableNotificationRow) view;
|
||||||
enr.dragAndDropSuccess();
|
enr.dragAndDropSuccess();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Fade out the drag surface in place instead of animating back to the
|
||||||
|
// start position now that the shade is closed
|
||||||
|
fadeOutAndRemoveDragSurface(dragEvent);
|
||||||
}
|
}
|
||||||
|
// Clear the drag listener set above
|
||||||
|
view.setOnDragListener(null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fadeOutAndRemoveDragSurface(DragEvent dragEvent) {
|
||||||
|
SurfaceControl dragSurface = dragEvent.getDragSurface();
|
||||||
|
SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
|
||||||
|
ValueAnimator returnAnimator = ValueAnimator.ofFloat(0f, 1f);
|
||||||
|
returnAnimator.setDuration(200);
|
||||||
|
returnAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||||
|
returnAnimator.addUpdateListener(animation -> {
|
||||||
|
float t = animation.getAnimatedFraction();
|
||||||
|
float alpha = 1f - t;
|
||||||
|
tx.setAlpha(dragSurface, alpha);
|
||||||
|
tx.apply();
|
||||||
|
});
|
||||||
|
returnAnimator.addListener(new AnimatorListenerAdapter() {
|
||||||
|
private boolean mCanceled = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationCancel(Animator animation) {
|
||||||
|
cleanUpSurface();
|
||||||
|
mCanceled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
if (mCanceled) {
|
||||||
|
// Already handled above
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cleanUpSurface();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanUpSurface() {
|
||||||
|
tx.remove(dragSurface);
|
||||||
|
tx.apply();
|
||||||
|
tx.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
returnAnimator.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.row;
|
|||||||
|
|
||||||
import static android.view.DragEvent.ACTION_DRAG_STARTED;
|
import static android.view.DragEvent.ACTION_DRAG_STARTED;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
import android.testing.TestableLooper;
|
import android.testing.TestableLooper;
|
||||||
@@ -36,7 +38,12 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyFloat;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -58,6 +65,7 @@ public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase {
|
|||||||
private NotificationMenuRow mMenuRow = mock(NotificationMenuRow.class);
|
private NotificationMenuRow mMenuRow = mock(NotificationMenuRow.class);
|
||||||
private NotificationMenuRowPlugin.MenuItem mMenuItem =
|
private NotificationMenuRowPlugin.MenuItem mMenuItem =
|
||||||
mock(NotificationMenuRowPlugin.MenuItem.class);
|
mock(NotificationMenuRowPlugin.MenuItem.class);
|
||||||
|
private ShadeController mShadeController = mock(ShadeController.class);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
@@ -69,11 +77,15 @@ public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase {
|
|||||||
mContext,
|
mContext,
|
||||||
mDependency,
|
mDependency,
|
||||||
TestableLooper.get(this));
|
TestableLooper.get(this));
|
||||||
mRow = mNotificationTestHelper.createRow();
|
mRow = spy(mNotificationTestHelper.createRow());
|
||||||
|
Notification notification = mRow.getEntry().getSbn().getNotification();
|
||||||
|
notification.contentIntent = mock(PendingIntent.class);
|
||||||
|
doReturn(true).when(mRow).startDragAndDrop(any(), any(), any(), anyInt());
|
||||||
mGroupRow = mNotificationTestHelper.createGroup(4);
|
mGroupRow = mNotificationTestHelper.createGroup(4);
|
||||||
when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
|
when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
|
||||||
|
|
||||||
mController = new ExpandableNotificationRowDragController(mContext, mHeadsUpManager);
|
mController = new ExpandableNotificationRowDragController(mContext, mHeadsUpManager,
|
||||||
|
mShadeController);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -86,10 +98,6 @@ public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase {
|
|||||||
mRow.doLongClickCallback(0, 0);
|
mRow.doLongClickCallback(0, 0);
|
||||||
mRow.doDragCallback(0, 0);
|
mRow.doDragCallback(0, 0);
|
||||||
verify(controller).startDragAndDrop(mRow);
|
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();
|
verify(mHeadsUpManager, times(1)).releaseAllImmediately();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,14 +106,27 @@ public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase {
|
|||||||
ExpandableNotificationRowDragController controller = createSpyController();
|
ExpandableNotificationRowDragController controller = createSpyController();
|
||||||
mRow.setDragController(controller);
|
mRow.setDragController(controller);
|
||||||
|
|
||||||
mDependency.get(ShadeController.class).instantExpandNotificationsPanel();
|
mRow.doDragCallback(0, 0);
|
||||||
|
verify(controller).startDragAndDrop(mRow);
|
||||||
|
verify(mShadeController).animateCollapsePanels(eq(0), eq(true),
|
||||||
|
eq(false), anyFloat());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoStartDrag_noLaunchIntent() throws Exception {
|
||||||
|
ExpandableNotificationRowDragController controller = createSpyController();
|
||||||
|
mRow.setDragController(controller);
|
||||||
|
|
||||||
|
// Clear the intents
|
||||||
|
Notification notification = mRow.getEntry().getSbn().getNotification();
|
||||||
|
notification.contentIntent = null;
|
||||||
|
notification.fullScreenIntent = null;
|
||||||
|
|
||||||
mRow.doDragCallback(0, 0);
|
mRow.doDragCallback(0, 0);
|
||||||
verify(controller).startDragAndDrop(mRow);
|
verify(controller).startDragAndDrop(mRow);
|
||||||
|
|
||||||
// Simulate the drag start
|
// Verify that we never start the actual drag since there is no content
|
||||||
mRow.dispatchDragEvent(DragEvent.obtain(ACTION_DRAG_STARTED, 0, 0, 0, 0, null, null, null,
|
verify(mRow, never()).startDragAndDrop(any(), any(), any(), anyInt());
|
||||||
null, null, false));
|
|
||||||
verify(mDependency.get(ShadeController.class)).animateCollapsePanels(0, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpandableNotificationRowDragController createSpyController() {
|
private ExpandableNotificationRowDragController createSpyController() {
|
||||||
|
|||||||
Reference in New Issue
Block a user