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

Commit d139b0aa authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Adding support for multiwindow drag and drop

Change-Id: I95b46e3c3f1238307d3ef5a6c81a8e530ba0987a
parent 1e35fbba
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -259,7 +259,8 @@ public abstract class ButtonDropTarget extends TextView
            }
        };
        dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
                DRAG_VIEW_DROP_DURATION, new DecelerateInterpolator(2),
                mLauncher.getDragController().isExternalDrag() ? 1 : DRAG_VIEW_DROP_DURATION,
                new DecelerateInterpolator(2),
                new LinearInterpolator(), onAnimationEndRunnable,
                DragLayer.ANIMATION_END_DISAPPEAR, null);
    }
+1 −1
Original line number Diff line number Diff line
@@ -158,7 +158,7 @@ public class ShortcutInfo extends ItemInfo {
     */
    Intent promisedIntent;

    ShortcutInfo() {
    public ShortcutInfo() {
        itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
    }

+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.launcher3.dragndrop;

import android.content.Context;
import android.view.View;

import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;

/**
 * DragSource used when the drag started at another window.
 */
public class AnotherWindowDragSource implements DragSource {

    private final Context mContext;

    AnotherWindowDragSource(Context context) {
        mContext = context;
    }

    @Override
    public boolean supportsFlingToDelete() {
        return false;
    }

    @Override
    public boolean supportsAppInfoDropTarget() {
        return false;
    }

    @Override
    public boolean supportsDeleteDropTarget() {
        return false;
    }

    @Override
    public float getIntrinsicIconScaleFactor() {
        return 1;
    }

    @Override
    public void onFlingToDeleteCompleted() {
    }

    @Override
    public void onDropCompleted(View target, DragObject d,
            boolean isFlingToDelete, boolean success) {
        if (!success) {
            Launcher.getLauncher(mContext).exitSpringLoadedDragModeDelayed(false, 0, null);
        }

    }

    @Override
    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
        // TODO: Probably log something
    }
}
+11 −3
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.view.DragEvent;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -213,6 +212,12 @@ public class DragController implements DragDriver.EventListener, TouchController
        }
        mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);

        mOptions = options;
        if (mOptions.systemDndStartPoint != null) {
            mMotionDownX = mOptions.systemDndStartPoint.x;
            mMotionDownY = mOptions.systemDndStartPoint.y;
        }

        final int registrationX = mMotionDownX - dragLayerX;
        final int registrationY = mMotionDownY - dragLayerY;

@@ -221,7 +226,6 @@ public class DragController implements DragDriver.EventListener, TouchController

        mLastDropTarget = null;

        mOptions = options;
        mDragObject = new DropTarget.DragObject();

        final Resources res = mLauncher.getResources();
@@ -241,7 +245,7 @@ public class DragController implements DragDriver.EventListener, TouchController
            mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
            mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);

            mDragDriver = DragDriver.create(this, dragInfo, dragView);
            mDragDriver = DragDriver.create(mLauncher, this, mDragObject, mOptions);
        }

        mDragObject.dragSource = source;
@@ -293,6 +297,10 @@ public class DragController implements DragDriver.EventListener, TouchController
        return mDragDriver != null || (mOptions != null && mOptions.isAccessibleDrag);
    }

    public boolean isExternalDrag() {
        return (mOptions != null && mOptions.systemDndStartPoint != null);
    }

    /**
     * Stop dragging without dropping.
     */
+69 −96
Original line number Diff line number Diff line
@@ -17,18 +17,19 @@
package com.android.launcher3.dragndrop;

import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Point;
import android.view.DragEvent;
import android.view.MotionEvent;
import android.view.View;

import com.android.launcher3.AnotherWindowDropTarget;
import com.android.launcher3.DropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;

import java.util.ArrayList;

/**
 * Base class for driving a drag/drop operation.
@@ -50,7 +51,7 @@ public abstract class DragDriver {
    /**
     * Handles ending of the DragView animation.
     */
    public abstract void onDragViewAnimationEnd();
    public void onDragViewAnimationEnd() { }

    public boolean onTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
@@ -89,100 +90,46 @@ public abstract class DragDriver {
        return true;
    }

    public static DragDriver create(
            DragController dragController, ItemInfo dragInfo, DragView dragView) {
        if (FeatureFlags.LAUNCHER3_USE_SYSTEM_DRAG_DRIVER && Utilities.isNycOrAbove()) {
            return new SystemDragDriver(dragController, dragInfo.getIntent(), dragView);
    public static DragDriver create(Context context, DragController dragController,
            DragObject dragObject, DragOptions options) {
        if (Utilities.isNycOrAbove() && options.systemDndStartPoint != null) {
            return new SystemDragDriver(dragController, context, dragObject);
        } else {
            return new InternalDragDriver(dragController);
        }
    }

};
}

/**
 * Class for driving a system (i.e. framework) drag/drop operation.
 */
class SystemDragDriver extends DragDriver {
    /** Intent associated with the drag operation, or null is there no associated intent.  */
    private final Intent mDragIntent;

    private final DragView mDragView;
    boolean mIsFrameworkDragActive = false;
    private final DragObject mDragObject;
    private final Context mContext;

    boolean mReceivedDropEvent = false;
    float mLastX = 0;
    float mLastY = 0;

    public SystemDragDriver(DragController dragController, Intent dragIntent, DragView dragView) {
    public SystemDragDriver(DragController dragController, Context context, DragObject dragObject) {
        super(dragController);
        mDragIntent = dragIntent;
        mDragView = dragView;
    }

    private static class ShadowBuilder extends View.DragShadowBuilder {
        final DragView mDragView;

        public ShadowBuilder(DragView dragView) {
            mDragView = dragView;
        }

        @Override
        public void onProvideShadowMetrics (Point size, Point touch) {
            mDragView.provideDragShadowMetrics(size, touch);
        }

        @Override
        public void onDrawShadow(Canvas canvas) {
            mDragView.drawDragShadow(canvas);
        }
    };

    @Override
    public void onDragViewAnimationEnd() {
        // Clip data for the drag operation. If there is an intent, create an intent-based ClipData,
        // which will be passed to a global DND.
        // If there is no intent, craft a fake ClipData and start a local DND operation; this
        // ClipData will be ignored.
        final ClipData dragData = mDragIntent != null ?
                ClipData.newIntent("", mDragIntent) :
                ClipData.newPlainText("", "");

        View.DragShadowBuilder shadowBuilder = new ShadowBuilder(mDragView);
        // TODO: DND flags are in flux, once settled, use the appropriate constant.
        final int flagGlobal = 1 << 0;
        final int flagOpaque = 1 << 9;
        final int flags = (mDragIntent != null ? flagGlobal : 0) | flagOpaque;

        mIsFrameworkDragActive = true;

        if (!mDragView.startDrag(dragData, shadowBuilder, null, flags)) {
            mIsFrameworkDragActive = false;
            mEventListener.onDriverDragCancel();
            return;
        }

        // Starting from this point, the driver takes over showing the drag shadow, so hiding the
        // drag view.
        mDragView.setVisibility(View.INVISIBLE);
        mDragObject = dragObject;
        mContext = context;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return !mIsFrameworkDragActive && super.onTouchEvent(ev);
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return !mIsFrameworkDragActive && super.onInterceptTouchEvent(ev);
        return false;
    }

    @Override
    public boolean onDragEvent (DragEvent event) {
        if (!mIsFrameworkDragActive) {
            // We are interested only in drag events started by this driver.
            return false;
        }

        final int action = event.getAction();

        switch (action) {
@@ -192,8 +139,6 @@ class SystemDragDriver extends DragDriver {
                return true;

            case DragEvent.ACTION_DRAG_ENTERED:
                mLastX = event.getX();
                mLastY = event.getY();
                return true;

            case DragEvent.ACTION_DRAG_LOCATION:
@@ -205,35 +150,66 @@ class SystemDragDriver extends DragDriver {
            case DragEvent.ACTION_DROP:
                mLastX = event.getX();
                mLastY = event.getY();
                mReceivedDropEvent = true;
                return true;
                mReceivedDropEvent =
                        updateInfoFromClipData(event.getClipData(), event.getClipDescription());
                return mReceivedDropEvent;

            case DragEvent.ACTION_DRAG_EXITED:
                mLastX = event.getX();
                mLastY = event.getY();
                mEventListener.onDriverDragExitWindow();
                return true;

            case DragEvent.ACTION_DRAG_ENDED:
                final boolean dragAccepted = event.getResult();
                final boolean acceptedByAnotherWindow = dragAccepted && !mReceivedDropEvent;
                if (mReceivedDropEvent) {
                    mEventListener.onDriverDragEnd(mLastX, mLastY, null);
                } else {
                    mEventListener.onDriverDragCancel();
                }
                return true;

                // When the system drag ends, its drag shadow disappears. Resume showing the drag
                // view for the possible final animation.
                mDragView.setVisibility(View.VISIBLE);
            default:
                return false;
        }
    }

    private boolean updateInfoFromClipData(ClipData data, ClipDescription desc) {
        if (data == null) {
            return false;
        }
        ArrayList<Intent> intents = new ArrayList<>();
        int itemCount = data.getItemCount();
        for (int i = 0; i < itemCount; i++) {
            Intent intent = data.getItemAt(i).getIntent();
            if (intent == null) {
                continue;
            }

                final DropTarget dropTargetOverride = acceptedByAnotherWindow ?
                        new AnotherWindowDropTarget(mDragView.getContext()) : null;
            // Give preference to shortcut intents.
            if (!Intent.ACTION_CREATE_SHORTCUT.equals(intent.getAction())) {
                intents.add(intent);
                continue;
            }
            ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, intent);
            if (info != null) {
                mDragObject.dragInfo = info;
                return true;
            }
            return true;
        }

                mEventListener.onDriverDragEnd(mLastX, mLastY, dropTargetOverride);
                mIsFrameworkDragActive = false;
        // Try creating shortcuts just using the intent and label
        Intent fullIntent = new Intent().putExtra(Intent.EXTRA_SHORTCUT_NAME, desc.getLabel());
        for (Intent intent : intents) {
            fullIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
            ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, fullIntent);
            if (info != null) {
                mDragObject.dragInfo = info;
                return true;
            }
        }

            default:
        return false;
    }
}
};

/**
 * Class for driving an internal (i.e. not using framework) drag/drop operation.
@@ -243,9 +219,6 @@ class InternalDragDriver extends DragDriver {
        super(dragController);
    }

    @Override
    public void onDragViewAnimationEnd() {}

    @Override
    public boolean onDragEvent (DragEvent event) { return false; }
};
Loading