Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d831d5c authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "Support launching an intent based drag into split" into main

parents 0fb77156 fec78497
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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>
+3 −5
Original line number Diff line number Diff line
@@ -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();
+12 −8
Original line number Diff line number Diff line
@@ -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;
    }

+58 −14
Original line number Diff line number Diff line
@@ -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,9 +54,11 @@ 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;
import android.util.Log;
import android.util.Slog;

import androidx.annotation.IntDef;
@@ -63,8 +67,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 +110,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 +231,7 @@ public class DragAndDropPolicy {
    }

    @VisibleForTesting
    void handleDrop(Target target, ClipData data) {
    void handleDrop(Target target) {
        if (target == null || !mTargets.contains(target)) {
            return;
        }
@@ -238,40 +246,76 @@ 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 (!user.equals(launchIntent.getCreatorUserHandle())) {
                    Log.e(TAG, "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.
     */
+79 −6
Original line number Diff line number Diff line
@@ -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);
    }
}
Loading