Skip to content
Snippets Groups Projects
Commit 89848002 authored by Winson Chung's avatar Winson Chung
Browse files

Support launching an intent based drag into split

- Extend support for launching intent drags into splitscreen in
  addition to crafted app drags sourced from SystemUI
- Unlike app drag drags which are consumed for the whole display,
  intent drags only invoke split from the side edges of the screen,
  so we need to manipulate the touchable region of the shell drop
  target based on the drag data in the current drag session

Bug: 320797628
Test: atest WMShellUnitTests
Test: https://recall.googleplex.com/projects/e3f080d7-2818-43f0-a087-405000b8fdf5/sessions/e88a0470-6850-43a0-abfa-3366b7c7c6a4

Change-Id: I04ddf3b463b446d2c688abed44e75fbd7f2110e1
parent accc7f1d
No related branches found
No related tags found
No related merge requests found
Showing
with 306 additions and 73 deletions
......@@ -101,6 +101,10 @@
<dimen name="split_divider_bar_width">10dp</dimen>
<dimen name="split_divider_corner_size">42dp</dimen>
<!-- The distance from the edge of the screen to invoke splitscreen when the user is dragging
an intent that can be launched into split. -->
<dimen name="drag_launchable_intent_edge_margin">48dp</dimen>
<!-- One-Handed Mode -->
<!-- Threshold for dragging distance to enable one-handed mode -->
<dimen name="gestures_onehanded_drag_threshold">20dp</dimen>
......
......@@ -59,7 +59,6 @@ import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
......@@ -316,12 +315,11 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
return false;
}
// TODO(b/290391688): Also update the session data with task stack changes
InstanceId loggerSessionId = mLogger.logStart(event);
pd.activeDragCount++;
pd.dragSession = new DragSession(mContext, ActivityTaskManager.getInstance(),
pd.dragSession = new DragSession(ActivityTaskManager.getInstance(),
mDisplayController.getDisplayLayout(displayId), event.getClipData());
pd.dragSession.update();
pd.dragLayout.prepare(pd.dragSession, loggerSessionId);
pd.activeDragCount++;
pd.dragLayout.prepare(pd.dragSession, mLogger.logStart(pd.dragSession));
setDropTargetWindowVisibility(pd, View.VISIBLE);
notifyListeners(l -> {
l.onDragStarted();
......
......@@ -53,17 +53,21 @@ public class DragAndDropEventLogger {
/**
* Logs the start of a drag.
*/
public InstanceId logStart(DragEvent event) {
final ClipDescription description = event.getClipDescription();
final ClipData data = event.getClipData();
final ClipData.Item item = data.getItemAt(0);
mInstanceId = item.getIntent().getParcelableExtra(
ClipDescription.EXTRA_LOGGING_INSTANCE_ID);
public InstanceId logStart(DragSession session) {
mInstanceId = session.appData != null
? session.appData.getParcelableExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID,
InstanceId.class)
: null;
if (mInstanceId == null) {
mInstanceId = mIdSequence.newInstanceId();
}
mActivityInfo = item.getActivityInfo();
log(getStartEnum(description), mActivityInfo);
mActivityInfo = session.activityInfo;
if (session.appData != null) {
log(getStartEnum(session.getClipDescription()), mActivityInfo);
} else {
// TODO(b/255649902): Update this once we have a new enum
log(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_ACTIVITY, mActivityInfo);
}
return mInstanceId;
}
......
......@@ -29,6 +29,8 @@ import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.EXTRA_SHORTCUT_ID;
import static android.content.Intent.EXTRA_TASK_ID;
import static android.content.Intent.EXTRA_USER;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
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_TOP_OR_LEFT;
......@@ -52,6 +54,7 @@ import android.content.pm.LauncherApps;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
......@@ -63,8 +66,10 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.lang.annotation.Retention;
......@@ -104,7 +109,9 @@ public class DragAndDropPolicy {
void start(DragSession session, InstanceId loggerSessionId) {
mLoggerSessionId = loggerSessionId;
mSession = session;
RectF disallowHitRegion = (RectF) mSession.dragData.getExtra(EXTRA_DISALLOW_HIT_REGION);
RectF disallowHitRegion = mSession.appData != null
? (RectF) mSession.appData.getExtra(EXTRA_DISALLOW_HIT_REGION)
: null;
if (disallowHitRegion == null) {
mDisallowHitRegion.setEmpty();
} else {
......@@ -223,7 +230,7 @@ public class DragAndDropPolicy {
}
@VisibleForTesting
void handleDrop(Target target, ClipData data) {
void handleDrop(Target target) {
if (target == null || !mTargets.contains(target)) {
return;
}
......@@ -238,40 +245,80 @@ public class DragAndDropPolicy {
mSplitScreen.onDroppedToSplit(position, mLoggerSessionId);
}
final ClipDescription description = data.getDescription();
final Intent dragData = mSession.dragData;
startClipDescription(description, dragData, position);
if (mSession.appData != null) {
launchApp(mSession, position);
} else {
launchIntent(mSession, position);
}
}
private void startClipDescription(ClipDescription description, Intent intent,
@SplitPosition int position) {
/**
* Launches an app provided by SysUI.
*/
private void launchApp(DragSession session, @SplitPosition int position) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching app data at position=%d",
position);
final ClipDescription description = session.getClipDescription();
final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic();
baseActivityOpts.setDisallowEnterPictureInPictureWhileLaunching(true);
final Bundle opts = baseActivityOpts.toBundle();
if (intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)) {
opts.putAll(intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS));
if (session.appData.hasExtra(EXTRA_ACTIVITY_OPTIONS)) {
opts.putAll(session.appData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS));
}
// Put BAL flags to avoid activity start aborted.
opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
final UserHandle user = session.appData.getParcelableExtra(EXTRA_USER);
if (isTask) {
final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
final int taskId = session.appData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
mStarter.startTask(taskId, position, opts);
} else if (isShortcut) {
final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
final String packageName = session.appData.getStringExtra(EXTRA_PACKAGE_NAME);
final String id = session.appData.getStringExtra(EXTRA_SHORTCUT_ID);
mStarter.startShortcut(packageName, id, position, opts, user);
} else {
final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
final PendingIntent launchIntent =
session.appData.getParcelableExtra(EXTRA_PENDING_INTENT);
if (Build.IS_DEBUGGABLE) {
if (launchIntent.isImmutable()) {
throw new IllegalStateException("Expected immutable app pending intent from "
+ "SysUI");
} else if (!user.equals(launchIntent.getCreatorUserHandle())) {
throw new IllegalStateException("Expected app intent's EXTRA_USER to match "
+ "pending intent user");
}
}
mStarter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */,
position, opts);
}
}
/**
* Launches an intent sender provided by an application.
*/
private void launchIntent(DragSession session, @SplitPosition int position) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching intent at position=%d",
position);
final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic();
baseActivityOpts.setDisallowEnterPictureInPictureWhileLaunching(true);
// TODO(b/255649902): Rework this so that SplitScreenController can always use the options
// instead of a fillInIntent since it's assuming that the PendingIntent is mutable
baseActivityOpts.setPendingIntentLaunchFlags(FLAG_ACTIVITY_NEW_TASK
| FLAG_ACTIVITY_MULTIPLE_TASK);
final Bundle opts = baseActivityOpts.toBundle();
// Put BAL flags to avoid activity start aborted.
opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
mStarter.startIntent(session.launchableIntent,
session.launchableIntent.getCreatorUserHandle().getIdentifier(),
null /* fillIntent */, position, opts);
}
/**
* Interface for actually committing the task launches.
*/
......
......@@ -22,6 +22,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS;
import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
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_TOP_OR_LEFT;
......@@ -38,12 +40,15 @@ import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.view.DragEvent;
import android.view.SurfaceControl;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.widget.LinearLayout;
......@@ -65,7 +70,8 @@ import java.util.ArrayList;
/**
* Coordinates the visible drop targets for the current drag within a single display.
*/
public class DragLayout extends LinearLayout {
public class DragLayout extends LinearLayout
implements ViewTreeObserver.OnComputeInternalInsetsListener {
// While dragging the status bar is hidden.
private static final int HIDE_STATUS_BAR_FLAGS = StatusBarManager.DISABLE_NOTIFICATION_ICONS
......@@ -90,7 +96,9 @@ public class DragLayout extends LinearLayout {
private int mDisplayMargin;
private int mDividerSize;
private int mLaunchIntentEdgeMargin;
private Insets mInsets = Insets.NONE;
private Region mTouchableRegion;
private boolean mIsShowing;
private boolean mHasDropped;
......@@ -106,10 +114,11 @@ public class DragLayout extends LinearLayout {
mStatusBarManager = context.getSystemService(StatusBarManager.class);
mLastConfiguration.setTo(context.getResources().getConfiguration());
mDisplayMargin = context.getResources().getDimensionPixelSize(
R.dimen.drop_layout_display_margin);
mDividerSize = context.getResources().getDimensionPixelSize(
R.dimen.split_divider_bar_width);
final Resources res = context.getResources();
mDisplayMargin = res.getDimensionPixelSize(R.dimen.drop_layout_display_margin);
mDividerSize = res.getDimensionPixelSize(R.dimen.split_divider_bar_width);
mLaunchIntentEdgeMargin =
res.getDimensionPixelSize(R.dimen.drag_launchable_intent_edge_margin);
// Always use LTR because we assume dropZoneView1 is on the left and 2 is on the right when
// showing the highlight.
......@@ -130,6 +139,66 @@ public class DragLayout extends LinearLayout {
updateContainerMargins(mIsLeftRightSplit);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mTouchableRegion = Region.obtain();
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
mTouchableRegion.recycle();
}
@Override
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inOutInfo) {
if (mSession != null && mSession.launchableIntent != null) {
inOutInfo.touchableRegion.set(mTouchableRegion);
inOutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
updateTouchableRegion();
}
/**
* Updates the touchable region, this should be called after any configuration changes have
* been applied.
*/
private void updateTouchableRegion() {
mTouchableRegion.setEmpty();
if (mSession != null && mSession.launchableIntent != null) {
final int width = getMeasuredWidth();
final int height = getMeasuredHeight();
if (mIsLeftRightSplit) {
mTouchableRegion.union(
new Rect(0, 0, mInsets.left + mLaunchIntentEdgeMargin, height));
mTouchableRegion.union(
new Rect(width - mInsets.right - mLaunchIntentEdgeMargin, 0, width,
height));
} else {
mTouchableRegion.union(
new Rect(0, 0, width, mInsets.top + mLaunchIntentEdgeMargin));
mTouchableRegion.union(
new Rect(0, height - mInsets.bottom - mLaunchIntentEdgeMargin, width,
height));
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"Updating drag layout width=%d height=%d touchable region=%s",
width, height, mTouchableRegion);
// Reapply insets to update the touchable region
requestApplyInsets();
}
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mInsets = insets.getInsets(Type.tappableElement() | Type.displayCutout());
......@@ -164,6 +233,7 @@ public class DragLayout extends LinearLayout {
mDropZoneView2.onThemeChange();
}
mLastConfiguration.setTo(newConfig);
requestLayout();
}
private void updateContainerMarginsForSingleTask() {
......@@ -242,6 +312,7 @@ public class DragLayout extends LinearLayout {
mSplitScreenController.getStageBounds(topOrLeftBounds, bottomOrRightBounds);
updateDropZoneSizes(topOrLeftBounds, bottomOrRightBounds);
}
requestLayout();
}
private void updateDropZoneSizesForSingleTask() {
......@@ -392,7 +463,7 @@ public class DragLayout extends LinearLayout {
mHasDropped = true;
// Process the drop
mPolicy.handleDrop(mCurrentTarget, event.getClipData());
mPolicy.handleDrop(mCurrentTarget);
// Start animating the drop UI out with the drag surface
hide(event, dropCompleteCallback);
......@@ -505,5 +576,7 @@ public class DragLayout extends LinearLayout {
pw.println(innerPrefix + "mIsShowing=" + mIsShowing);
pw.println(innerPrefix + "mHasDropped=" + mHasDropped);
pw.println(innerPrefix + "mCurrentTarget=" + mCurrentTarget);
pw.println(innerPrefix + "mInsets=" + mInsets);
pw.println(innerPrefix + "mTouchableRegion=" + mTouchableRegion);
}
}
......@@ -21,12 +21,15 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.content.ClipData;
import android.content.Context;
import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import androidx.annotation.Nullable;
import com.android.wm.shell.common.DisplayLayout;
import java.util.List;
......@@ -39,7 +42,18 @@ public class DragSession {
private final ClipData mInitialDragData;
final DisplayLayout displayLayout;
Intent dragData;
// The activity info associated with the activity in the appData or the launchableIntent
@Nullable
ActivityInfo activityInfo;
// The intent bundle that includes data about an app-type drag that is started by
// Launcher/SysUI. Only one of appDragData OR launchableIntent will be non-null for a session.
@Nullable
Intent appData;
// A launchable intent that is specified in the ClipData directly.
// Only one of appDragData OR launchableIntent will be non-null for a session.
@Nullable
PendingIntent launchableIntent;
// Stores the current running task at the time that the drag was initiated
ActivityManager.RunningTaskInfo runningTaskInfo;
@WindowConfiguration.WindowingMode
int runningTaskWinMode = WINDOWING_MODE_UNDEFINED;
......@@ -47,13 +61,21 @@ public class DragSession {
int runningTaskActType = ACTIVITY_TYPE_STANDARD;
boolean dragItemSupportsSplitscreen;
DragSession(Context context, ActivityTaskManager activityTaskManager,
DragSession(ActivityTaskManager activityTaskManager,
DisplayLayout dispLayout, ClipData data) {
mActivityTaskManager = activityTaskManager;
mInitialDragData = data;
displayLayout = dispLayout;
}
/**
* Returns the clip description associated with the drag.
* @return
*/
ClipDescription getClipDescription() {
return mInitialDragData.getDescription();
}
/**
* Updates the session data based on the current state of the system.
*/
......@@ -67,9 +89,11 @@ public class DragSession {
runningTaskActType = task.getActivityType();
}
final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo();
dragItemSupportsSplitscreen = info == null
|| ActivityInfo.isResizeableMode(info.resizeMode);
dragData = mInitialDragData.getItemAt(0).getIntent();
activityInfo = mInitialDragData.getItemAt(0).getActivityInfo();
// TODO: This should technically check & respect config_supportsNonResizableMultiWindow
dragItemSupportsSplitscreen = activityInfo == null
|| ActivityInfo.isResizeableMode(activityInfo.resizeMode);
appData = mInitialDragData.getItemAt(0).getIntent();
launchableIntent = DragUtils.getLaunchIntent(mInitialDragData);
}
}
......@@ -36,8 +36,21 @@ public class DragUtils {
* Returns whether we can handle this particular drag.
*/
public static boolean canHandleDrag(DragEvent event) {
return event.getClipData().getItemCount() > 0
&& (isAppDrag(event.getClipDescription()));
if (event.getClipData().getItemCount() <= 0) {
// No clip data, ignore this drag
return false;
}
if (isAppDrag(event.getClipDescription())) {
// Clip data contains an app drag initiated from SysUI, handle it
return true;
}
if (com.android.window.flags.Flags.delegateUnhandledDrags()
&& getLaunchIntent(event) != null) {
// Clip data contains a launchable intent drag, handle it
return true;
}
// Otherwise ignore
return false;
}
/**
......
......@@ -806,6 +806,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Override
public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"startIntent(): intent=%s user=%d fillInIntent=%s position=%d", intent, userId1,
fillInIntent, position);
// Flag this as a no-user-action launch to prevent sending user leaving event to the current
// top activity since it's going to be put into another side of the split. This prevents the
// current top activity from going into pip mode due to user leaving event.
......@@ -824,6 +827,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
.map(recentTasks -> recentTasks.findTaskInBackground(component, userId1))
.orElse(null);
if (taskInfo != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Found suitable background task=%s", taskInfo);
if (ENABLE_SHELL_TRANSITIONS) {
mStageCoordinator.startTask(taskInfo.taskId, position, options);
} else {
......
......@@ -22,9 +22,11 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
import static android.content.ClipDescription.MIMETYPE_TEXT_INTENT;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
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_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
......@@ -46,6 +48,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.quality.Strictness.LENIENT;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
......@@ -61,6 +65,7 @@ import android.content.res.Resources;
import android.graphics.Insets;
import android.os.RemoteException;
import android.view.DisplayInfo;
import android.view.DragEvent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
......@@ -70,12 +75,15 @@ import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.TaskSnapshotWindow;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import java.util.ArrayList;
import java.util.Collections;
......@@ -107,6 +115,7 @@ public class DragAndDropPolicyTest extends ShellTestCase {
private DragAndDropPolicy mPolicy;
private ClipData mActivityClipData;
private ClipData mLaunchableIntentClipData;
private ClipData mNonResizeableActivityClipData;
private ClipData mTaskClipData;
private ClipData mShortcutClipData;
......@@ -115,9 +124,16 @@ public class DragAndDropPolicyTest extends ShellTestCase {
private ActivityManager.RunningTaskInfo mFullscreenAppTask;
private ActivityManager.RunningTaskInfo mNonResizeableFullscreenAppTask;
private MockitoSession mMockitoSession;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mMockitoSession = mockitoSession()
.strictness(LENIENT)
.mockStatic(DragUtils.class)
.startMocking();
when(DragUtils.canHandleDrag(any())).thenReturn(true);
Resources res = mock(Resources.class);
Configuration config = new Configuration();
......@@ -134,11 +150,12 @@ public class DragAndDropPolicyTest extends ShellTestCase {
mInsets = Insets.of(0, 0, 0, 0);
mPolicy = spy(new DragAndDropPolicy(mContext, mSplitScreenStarter, mSplitScreenStarter));
mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
mActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
mLaunchableIntentClipData = createIntentClipData();
mNonResizeableActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
setClipDataResizeable(mNonResizeableActivityClipData, false);
mTaskClipData = createClipData(MIMETYPE_APPLICATION_TASK);
mShortcutClipData = createClipData(MIMETYPE_APPLICATION_SHORTCUT);
mTaskClipData = createAppClipData(MIMETYPE_APPLICATION_TASK);
mShortcutClipData = createAppClipData(MIMETYPE_APPLICATION_SHORTCUT);
mHomeTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
mFullscreenAppTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
......@@ -149,10 +166,15 @@ public class DragAndDropPolicyTest extends ShellTestCase {
setRunningTask(mFullscreenAppTask);
}
@After
public void tearDown() {
mMockitoSession.finishMocking();
}
/**
* Creates a clip data that is by default resizeable.
* Creates an app-based clip data that is by default resizeable.
*/
private ClipData createClipData(String mimeType) {
private ClipData createAppClipData(String mimeType) {
ClipDescription clipDescription = new ClipDescription(mimeType, new String[] { mimeType });
Intent i = new Intent();
switch (mimeType) {
......@@ -164,7 +186,9 @@ public class DragAndDropPolicyTest extends ShellTestCase {
i.putExtra(Intent.EXTRA_TASK_ID, 12345);
break;
case MIMETYPE_APPLICATION_ACTIVITY:
i.putExtra(ClipDescription.EXTRA_PENDING_INTENT, mock(PendingIntent.class));
final PendingIntent pi = mock(PendingIntent.class);
doReturn(android.os.Process.myUserHandle()).when(pi).getCreatorUserHandle();
i.putExtra(ClipDescription.EXTRA_PENDING_INTENT, pi);
break;
}
i.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
......@@ -175,6 +199,22 @@ public class DragAndDropPolicyTest extends ShellTestCase {
return data;
}
/**
* Creates an intent-based clip data that is by default resizeable.
*/
private ClipData createIntentClipData() {
ClipDescription clipDescription = new ClipDescription("Intent",
new String[] { MIMETYPE_TEXT_INTENT });
PendingIntent intent = mock(PendingIntent.class);
when(intent.getCreatorUserHandle()).thenReturn(android.os.Process.myUserHandle());
ClipData.Item item = new ClipData.Item.Builder()
.setIntentSender(intent.getIntentSender())
.build();
ClipData data = new ClipData(clipDescription, item);
when(DragUtils.getLaunchIntent((ClipData) any())).thenReturn(intent);
return data;
}
private ActivityManager.RunningTaskInfo createTaskInfo(int winMode, int actType) {
ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.configuration.windowConfiguration.setActivityType(actType);
......@@ -204,58 +244,85 @@ public class DragAndDropPolicyTest extends ShellTestCase {
@Test
public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
dragOverFullscreenHome_expectOnlyFullscreenTarget(mActivityClipData);
}
@Test
public void testDragAppOverFullscreenApp_expectSplitScreenTargets() {
dragOverFullscreenApp_expectSplitScreenTargets(mActivityClipData);
}
@Test
public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(mActivityClipData);
}
@Test
public void testDragIntentOverFullscreenHome_expectOnlyFullscreenTarget() {
dragOverFullscreenHome_expectOnlyFullscreenTarget(mLaunchableIntentClipData);
}
@Test
public void testDragIntentOverFullscreenApp_expectSplitScreenTargets() {
dragOverFullscreenApp_expectSplitScreenTargets(mLaunchableIntentClipData);
}
@Test
public void testDragIntentOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(mLaunchableIntentClipData);
}
private void dragOverFullscreenHome_expectOnlyFullscreenTarget(ClipData data) {
doReturn(true).when(mSplitScreenStarter).isLeftRightSplit();
setRunningTask(mHomeTask);
DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
mLandscapeDisplayLayout, mActivityClipData);
DragSession dragSession = new DragSession(mActivityTaskManager,
mLandscapeDisplayLayout, data);
dragSession.update();
mPolicy.start(dragSession, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN));
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
eq(SPLIT_POSITION_UNDEFINED), any());
}
@Test
public void testDragAppOverFullscreenApp_expectSplitScreenTargets() {
private void dragOverFullscreenApp_expectSplitScreenTargets(ClipData data) {
doReturn(true).when(mSplitScreenStarter).isLeftRightSplit();
setRunningTask(mFullscreenAppTask);
DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
mLandscapeDisplayLayout, mActivityClipData);
DragSession dragSession = new DragSession(mActivityTaskManager,
mLandscapeDisplayLayout, data);
dragSession.update();
mPolicy.start(dragSession, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), mActivityClipData);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT));
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
eq(SPLIT_POSITION_TOP_OR_LEFT), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT));
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
private void dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(ClipData data) {
doReturn(false).when(mSplitScreenStarter).isLeftRightSplit();
setRunningTask(mFullscreenAppTask);
DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
mPortraitDisplayLayout, mActivityClipData);
DragSession dragSession = new DragSession(mActivityTaskManager,
mPortraitDisplayLayout, data);
dragSession.update();
mPolicy.start(dragSession, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), mActivityClipData);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP));
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
eq(SPLIT_POSITION_TOP_OR_LEFT), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM));
verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(),
eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
......@@ -263,7 +330,7 @@ public class DragAndDropPolicyTest extends ShellTestCase {
@Test
public void testTargetHitRects() {
setRunningTask(mFullscreenAppTask);
DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
DragSession dragSession = new DragSession(mActivityTaskManager,
mLandscapeDisplayLayout, mActivityClipData);
dragSession.update();
mPolicy.start(dragSession, mLoggerSessionId);
......
......@@ -216,8 +216,7 @@ class DragState {
mIsClosing = true;
// Unregister the input interceptor.
if (mInputInterceptor != null) {
if (DEBUG_DRAG)
Slog.d(TAG_WM, "unregistering drag input channel");
if (DEBUG_DRAG) Slog.d(TAG_WM, "Unregistering drag input channel");
// Input channel should be disposed on the thread where the input is being handled.
mDragDropController.sendHandlerMessage(
......@@ -227,9 +226,7 @@ class DragState {
// Send drag end broadcast if drag start has been sent.
if (mDragInProgress) {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "broadcasting DRAG_ENDED");
}
if (DEBUG_DRAG) Slog.d(TAG_WM, "Broadcasting DRAG_ENDED");
for (WindowState ws : mNotifiedWindows) {
float x = 0;
float y = 0;
......@@ -248,6 +245,7 @@ class DragState {
x, y, mThumbOffsetX, mThumbOffsetY, null, null, null, dragSurface, null,
mDragResult);
try {
if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DRAG_ENDED to " + ws);
ws.mClient.dispatchDragEvent(event);
} catch (RemoteException e) {
Slog.w(TAG_WM, "Unable to drag-end window " + ws);
......@@ -364,7 +362,7 @@ class DragState {
return false;
}
if (DEBUG_DRAG) Slog.d(TAG_WM, "sending DROP to " + touchedWin);
if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DROP to " + touchedWin);
final IBinder clientToken = touchedWin.mClient.asBinder();
final DragEvent event = createDropEvent(x, y, touchedWin, false /* includePrivateInfo */);
......@@ -460,7 +458,7 @@ class DragState {
*/
CompletableFuture<Void> register(Display display) {
display.getRealSize(mDisplaySize);
if (DEBUG_DRAG) Slog.d(TAG_WM, "registering drag input channel");
if (DEBUG_DRAG) Slog.d(TAG_WM, "Registering drag input channel");
if (mInputInterceptor != null) {
Slog.e(TAG_WM, "Duplicate register of drag input channel");
return completedFuture(null);
......@@ -489,7 +487,7 @@ class DragState {
mSourceUserId, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
Slog.d(TAG_WM, "Broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
}
final boolean containsAppExtras = containsApplicationExtras(mDataDescription);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment