OSDN Git Service

Adding support for drag and drop for requestPinItem.
authorSunny Goyal <sunnygoyal@google.com>
Sat, 21 Jan 2017 03:32:31 +0000 (19:32 -0800)
committerSunny Goyal <sunnygoyal@google.com>
Wed, 25 Jan 2017 06:24:54 +0000 (22:24 -0800)
On long pressing, the confirmation activity starts a system
drag-n-drop and focuses the launcher activity. We then drive
the launcher drag controller using the system drag event

Caveats:
> We use a transparent preview for system drag and drop and use
  a view inside launcher for actual preview. This gives us better
  control over various animations.
> The parameters for drag operation are passed to the Launcher
  activity using the intent. Since onNewIntent and onDragEvent
  come at different times and are not associated, a random uuid
  is used as mime-type to match the drag event with intent params
> If the workspace is locked (eg, loader is running) the drag
  operation is simply dropped. Will be imporved in follow up cls

Bug: 33584624
Change-Id: I0bb5b25b690f86b6af31a14e11beb669fcb3a281

15 files changed:
AndroidManifest-common.xml
src/com/android/launcher3/AnotherWindowDropTarget.java [deleted file]
src/com/android/launcher3/Launcher.java
src/com/android/launcher3/compat/PinItemRequestCompat.java
src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
src/com/android/launcher3/dragndrop/AddItemActivity.java
src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java [deleted file]
src/com/android/launcher3/dragndrop/DragController.java
src/com/android/launcher3/dragndrop/DragDriver.java
src/com/android/launcher3/dragndrop/DragLayer.java
src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java [deleted file]
src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
src/com/android/launcher3/dragndrop/PinItemDragListener.java [new file with mode: 0644]
src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
src/com/android/launcher3/widget/WidgetCell.java

index 974b0df..e112b9d 100644 (file)
@@ -81,6 +81,8 @@
 
         <activity android:name="com.android.launcher3.dragndrop.AddItemActivity"
             android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
+            android:excludeFromRecents="true"
+            android:autoRemoveFromRecents="true"
             android:label="@string/action_add_to_workspace" >
             <intent-filter>
                 <action android:name="android.content.pm.action.CONFIRM_PIN_ITEM" />
diff --git a/src/com/android/launcher3/AnotherWindowDropTarget.java b/src/com/android/launcher3/AnotherWindowDropTarget.java
deleted file mode 100644 (file)
index 052e5d0..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2015 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;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.graphics.Rect;
-
-/**
- * Drop target used when another window (i.e. another process) has accepted a global system drag.
- * If the accepted item was a shortcut, we delete it from Launcher.
- */
-public class AnotherWindowDropTarget implements DropTarget {
-    final Launcher mLauncher;
-
-    public AnotherWindowDropTarget (Context context) { mLauncher = Launcher.getLauncher(context); }
-
-    @Override
-    public boolean isDropEnabled() { return true; }
-
-    @Override
-    public void onDrop(DragObject dragObject) {
-        dragObject.deferDragViewCleanupPostAnimation = false;
-        LauncherModel.deleteItemFromDatabase(mLauncher, (ShortcutInfo) dragObject.dragInfo);
-    }
-
-    @Override
-    public void onDragEnter(DragObject dragObject) {}
-
-    @Override
-    public void onDragOver(DragObject dragObject) {}
-
-    @Override
-    public void onDragExit(DragObject dragObject) {}
-
-    @Override
-    public boolean acceptDrop(DragObject dragObject) {
-        return dragObject.dragInfo instanceof ShortcutInfo;
-    }
-
-    @Override
-    public void prepareAccessibilityDrop() {}
-
-    // These methods are implemented in Views
-    @Override
-    public void getHitRectRelativeToDragLayer(Rect outRect) {}
-}
index 8d8a70c..8dfc211 100644 (file)
@@ -49,6 +49,7 @@ import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.StrictMode;
 import android.os.SystemClock;
@@ -94,6 +95,7 @@ import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.PinItemDragListener;
 import com.android.launcher3.dynamicui.ExtractedColors;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
@@ -826,7 +828,7 @@ public class Launcher extends BaseActivity
     }
 
     @Override
-    protected void onActivityResult(
+    public void onActivityResult(
             final int requestCode, final int resultCode, final Intent data) {
         handleActivityResult(requestCode, resultCode, data);
         if (mLauncherCallbacks != null) {
@@ -1752,6 +1754,14 @@ public class Launcher extends BaseActivity
             if (mLauncherCallbacks != null) {
                 mLauncherCallbacks.onHomeIntent();
             }
+
+            Parcelable dragExtra = intent
+                    .getParcelableExtra(PinItemDragListener.EXTRA_PIN_ITEM_DRAG_LISTENER);
+            if (dragExtra instanceof PinItemDragListener) {
+                PinItemDragListener dragListener = (PinItemDragListener) dragExtra;
+                dragListener.setLauncher(this);
+                mDragLayer.setOnDragListener(dragListener);
+            }
         }
 
         if (mLauncherCallbacks != null) {
index 74d7765..481310a 100644 (file)
@@ -21,13 +21,14 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
 import android.os.Bundle;
+import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
  * A wrapper around platform implementation of PinItemRequestCompat until the
  * updated SDK is available.
  */
-public class PinItemRequestCompat {
+public class PinItemRequestCompat implements Parcelable {
 
     public static final String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
 
@@ -83,6 +84,32 @@ public class PinItemRequestCompat {
         }
     }
 
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int i) {
+        parcel.writeParcelable(mObject, i);
+    }
+
+    public Intent toIntent() {
+        return new Intent().putExtra(EXTRA_PIN_ITEM_REQUEST, mObject);
+    }
+
+    public static final Parcelable.Creator<PinItemRequestCompat> CREATOR =
+            new Parcelable.Creator<PinItemRequestCompat>() {
+                public PinItemRequestCompat createFromParcel(Parcel source) {
+                    Parcelable object = source.readParcelable(null);
+                    return new PinItemRequestCompat(object);
+                }
+
+                public PinItemRequestCompat[] newArray(int size) {
+                    return new PinItemRequestCompat[size];
+                }
+            };
+
     public static PinItemRequestCompat getPinItemRequest(Intent intent) {
         Parcelable extra = intent.getParcelableExtra(EXTRA_PIN_ITEM_REQUEST);
         return extra == null ? null : new PinItemRequestCompat(extra);
index ece7759..1cfbd20 100644 (file)
@@ -35,6 +35,7 @@ import android.util.Log;
 import android.widget.Toast;
 
 import com.android.launcher3.IconCache;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 
@@ -67,7 +68,7 @@ public abstract class ShortcutConfigActivityInfo {
 
     public abstract Drawable getFullResIcon(IconCache cache);
 
-    public boolean startConfigActivity(Activity activity, int requestCode) {
+    public boolean startConfigActivity(Launcher activity, int requestCode) {
         Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT)
                 .setComponent(getComponent());
         try {
@@ -136,7 +137,7 @@ public abstract class ShortcutConfigActivityInfo {
         }
 
         @Override
-        public boolean startConfigActivity(Activity activity, int requestCode) {
+        public boolean startConfigActivity(Launcher activity, int requestCode) {
             if (getUser().equals(Process.myUserHandle())) {
                 return super.startConfigActivity(activity, requestCode);
             }
index 6c6f141..423fdab 100644 (file)
 package com.android.launcher3.dragndrop;
 
 import android.annotation.TargetApi;
+import android.app.ActivityOptions;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.Intent;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Build;
 import android.os.Bundle;
+import android.util.Log;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.*;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.InstallShortcutReceiver;
@@ -38,13 +49,18 @@ import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.WidgetCell;
 import com.android.launcher3.widget.WidgetHostViewLoader;
+import com.android.launcher3.widget.WidgetImageView;
 
 @TargetApi(Build.VERSION_CODES.N_MR1)
-public class AddItemActivity extends BaseActivity {
+public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
+
+    private static final int SHADOW_SIZE = 10;
 
     private static final int REQUEST_BIND_APPWIDGET = 1;
     private static final String STATE_EXTRA_WIDGET_ID = "state.widget.id";
 
+    private final PointF mLastTouchPos = new PointF();
+
     private PinItemRequestCompat mRequest;
     private LauncherAppState mApp;
     private InvariantDeviceProfile mIdp;
@@ -86,11 +102,54 @@ public class AddItemActivity extends BaseActivity {
                 finish();
             }
         }
+
+        mWidgetCell.setOnTouchListener(this);
+        mWidgetCell.setOnLongClickListener(this);
+    }
+
+    @Override
+    public boolean onTouch(View view, MotionEvent motionEvent) {
+        mLastTouchPos.set(motionEvent.getX(), motionEvent.getY());
+        return false;
+    }
+
+    @Override
+    public boolean onLongClick(View view) {
+        // Find the position of the preview relative to the touch location.
+        WidgetImageView img = mWidgetCell.getWidgetView();
+        Rect bounds = img.getBitmapBounds();
+        bounds.offset(img.getLeft() - (int) mLastTouchPos.x, img.getTop() - (int) mLastTouchPos.y);
+
+        // Start home and pass the draw request params
+        PinItemDragListener listener = new PinItemDragListener(mRequest, bounds);
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setPackage(getPackageName())
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                .putExtra(PinItemDragListener.EXTRA_PIN_ITEM_DRAG_LISTENER, listener);
+        startActivity(homeIntent,
+                ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
+
+        // Start a system drag and drop. We use a transparent bitmap as preview for system drag
+        // as the preview is handled internally by launcher.
+        ClipDescription description = new ClipDescription("", new String[]{listener.getMimeType()});
+        ClipData data = new ClipData(description, new ClipData.Item(""));
+        view.startDragAndDrop(data, new DragShadowBuilder(view) {
+
+            @Override
+            public void onDrawShadow(Canvas canvas) { }
+
+            @Override
+            public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
+                outShadowSize.set(SHADOW_SIZE, SHADOW_SIZE);
+                outShadowTouchPoint.set(SHADOW_SIZE / 2, SHADOW_SIZE / 2);
+            }
+        }, null, View.DRAG_FLAG_GLOBAL);
+        return false;
     }
 
     private void setupShortcut() {
-        WidgetItem item = new WidgetItem(new PinShortcutRequestActivityInfo(
-                mRequest.getShortcutInfo(), this));
+        WidgetItem item = new WidgetItem(new PinShortcutRequestActivityInfo(mRequest, this));
         mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
         mWidgetCell.ensurePreview();
     }
diff --git a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
deleted file mode 100644 (file)
index 1623010..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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 supportsAppInfoDropTarget() {
-        return false;
-    }
-
-    @Override
-    public boolean supportsDeleteDropTarget() {
-        return false;
-    }
-
-    @Override
-    public float getIntrinsicIconScaleFactor() {
-        return 1;
-    }
-
-    @Override
-    public void onDropCompleted(View target, DragObject d,
-            boolean isFlingToDelete, boolean success) {
-        if (!success) {
-            Launcher.getLauncher(mContext).exitSpringLoadedDragModeDelayed(false, 0, null);
-        }
-
-    }
-
-    @Override
-    public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
-        // TODO: Probably log something
-    }
-}
index 745776d..80c2860 100644 (file)
@@ -36,7 +36,6 @@ import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.TouchController;
@@ -447,7 +446,8 @@ public class DragController implements DragDriver.EventListener, TouchController
     /**
      * Call this from a drag source view.
      */
-    public boolean onDragEvent(DragEvent event) {
+    public boolean onDragEvent(long dragStartTime, DragEvent event) {
+        mFlingToDeleteHelper.recordDragEvent(dragStartTime, event);
         return mDragDriver != null && mDragDriver.onDragEvent(event);
     }
 
index a90cfff..65c0f29 100644 (file)
 
 package com.android.launcher3.dragndrop;
 
-import android.content.ClipData;
-import android.content.ClipDescription;
 import android.content.Context;
-import android.content.Intent;
 import android.view.DragEvent;
 import android.view.MotionEvent;
 
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.InstallShortcutReceiver;
-import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 
-import java.util.ArrayList;
-
 /**
  * Base class for driving a drag/drop operation.
  */
@@ -107,7 +100,6 @@ class SystemDragDriver extends DragDriver {
     private final DragObject mDragObject;
     private final Context mContext;
 
-    boolean mReceivedDropEvent = false;
     float mLastX = 0;
     float mLastY = 0;
 
@@ -149,65 +141,21 @@ class SystemDragDriver extends DragDriver {
             case DragEvent.ACTION_DROP:
                 mLastX = event.getX();
                 mLastY = event.getY();
-                mReceivedDropEvent =
-                        updateInfoFromClipData(event.getClipData(), event.getClipDescription());
-                return mReceivedDropEvent;
-
+                mEventListener.onDriverDragMove(event.getX(), event.getY());
+                mEventListener.onDriverDragEnd(mLastX, mLastY);
+                return true;
             case DragEvent.ACTION_DRAG_EXITED:
                 mEventListener.onDriverDragExitWindow();
                 return true;
 
             case DragEvent.ACTION_DRAG_ENDED:
-                if (mReceivedDropEvent) {
-                    mEventListener.onDriverDragEnd(mLastX, mLastY);
-                } else {
-                    mEventListener.onDriverDragCancel();
-                }
+                mEventListener.onDriverDragCancel();
                 return true;
 
             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;
-            }
-
-            // 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;
-        }
-
-        // 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;
-            }
-        }
-
-        return false;
-    }
 }
 
 /**
index 4279cc3..de416e3 100644 (file)
@@ -21,21 +21,13 @@ import android.animation.AnimatorListenerAdapter;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.TargetApi;
-import android.content.ClipDescription;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.Build;
 import android.util.AttributeSet;
-import android.view.DragEvent;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -59,9 +51,7 @@ import com.android.launcher3.LauncherAppWidgetHostView;
 import com.android.launcher3.PinchToOverviewListener;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
@@ -373,49 +363,6 @@ public class DragLayer extends InsettableFrameLayout {
         return false;
     }
 
-    @TargetApi(Build.VERSION_CODES.N)
-    private void handleSystemDragStart(DragEvent event) {
-        if (!FeatureFlags.LAUNCHER3_USE_SYSTEM_DRAG_DRIVER || !Utilities.ATLEAST_NOUGAT) {
-            return;
-        }
-        if (mLauncher.isWorkspaceLocked()) {
-            return;
-        }
-
-        ClipDescription description = event.getClipDescription();
-        if (!description.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
-            return;
-        }
-        ShortcutInfo info = new ShortcutInfo();
-        // Set a dummy intent until we get the final value
-        info.intent = new Intent();
-
-        // Since we are not going through the workspace for starting the drag, set drag related
-        // information on the workspace before starting the drag.
-        ExternalDragPreviewProvider previewProvider =
-                new ExternalDragPreviewProvider(mLauncher, info);
-        mLauncher.getWorkspace().prepareDragWithProvider(previewProvider);
-
-        DragOptions options = new DragOptions();
-        options.systemDndStartPoint = new Point((int) event.getX(), (int) event.getY());
-
-        int halfPadding = previewProvider.previewPadding / 2;
-        mDragController.startDrag(
-                Bitmap.createBitmap(1, 1, Config.ARGB_8888),
-                0, 0,
-                new AnotherWindowDragSource(mLauncher), info,
-                new Point(- halfPadding, halfPadding),
-                previewProvider.getPreviewBounds(), 1f, options);
-    }
-
-    @Override
-    public boolean onDragEvent (DragEvent event) {
-        if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
-            handleSystemDragStart(event);
-        }
-        return mDragController.onDragEvent(event);
-    }
-
     /**
      * Determine the rect of the descendant in this DragLayer's coordinates
      *
diff --git a/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java b/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
deleted file mode 100644 (file)
index e558487..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.graphics.DragPreviewProvider;
-import com.android.launcher3.graphics.HolographicOutlineHelper;
-
-/**
- * Extension of {@link DragPreviewProvider} which provides a dummy outline when drag starts from
- * a different window.
- * It just draws an empty circle to a placeholder outline.
- */
-public class ExternalDragPreviewProvider extends DragPreviewProvider {
-
-    private final Launcher mLauncher;
-    private final ItemInfo mAddInfo;
-
-    private final int[] mOutlineSize;
-
-    public ExternalDragPreviewProvider(Launcher launcher, ItemInfo addInfo) {
-        super(null, launcher);
-        mLauncher = launcher;
-        mAddInfo = addInfo;
-
-        mOutlineSize = mLauncher.getWorkspace().estimateItemSize(mAddInfo, false, false);
-    }
-
-    public Rect getPreviewBounds() {
-        Rect rect = new Rect();
-        DeviceProfile dp = mLauncher.getDeviceProfile();
-        rect.left = blurSizeOutline / 2;
-        rect.top = (mOutlineSize[1] - dp.cellHeightPx) / 2;
-        rect.right = rect.left + dp.iconSizePx;
-        rect.bottom = rect.top + dp.iconSizePx;
-        return rect;
-    }
-
-    @Override
-    public Bitmap createDragOutline(Canvas canvas) {
-        final Bitmap b = Bitmap.createBitmap(mOutlineSize[0], mOutlineSize[1], Bitmap.Config.ALPHA_8);
-        canvas.setBitmap(b);
-
-        Paint paint = new Paint();
-        paint.setColor(Color.WHITE);
-        paint.setStyle(Paint.Style.FILL);
-
-        // Use 0.9f times the radius for the actual circle to account for icon normalization.
-        float radius = getPreviewBounds().width() * 0.5f;
-        canvas.drawCircle(blurSizeOutline / 2 + radius,
-                blurSizeOutline / 2 + radius, radius * 0.9f, paint);
-
-        HolographicOutlineHelper.getInstance(mLauncher).applyExpensiveOutlineWithBlur(b, canvas);
-        canvas.setBitmap(null);
-        return b;
-    }
-}
index a2aa27d..e794744 100644 (file)
@@ -17,6 +17,8 @@
 package com.android.launcher3.dragndrop;
 
 import android.graphics.PointF;
+import android.os.SystemClock;
+import android.view.DragEvent;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
@@ -53,6 +55,31 @@ public class FlingToDeleteHelper {
         mVelocityTracker.addMovement(ev);
     }
 
+    /**
+     * Same as {@link #recordMotionEvent}. It creates a temporary {@link MotionEvent} object
+     * using {@param event} for tracking velocity.
+     */
+    public void recordDragEvent(long dragStartTime, DragEvent event) {
+        final int motionAction;
+        switch (event.getAction()) {
+            case DragEvent.ACTION_DRAG_STARTED:
+                motionAction = MotionEvent.ACTION_DOWN;
+                break;
+            case DragEvent.ACTION_DRAG_LOCATION:
+                motionAction = MotionEvent.ACTION_MOVE;
+                break;
+            case DragEvent.ACTION_DRAG_ENDED:
+                motionAction = MotionEvent.ACTION_UP;
+                break;
+            default:
+                return;
+        }
+        MotionEvent emulatedEvent = MotionEvent.obtain(dragStartTime, SystemClock.uptimeMillis(),
+                motionAction, event.getX(), event.getY(), 0);
+        recordMotionEvent(emulatedEvent);
+        emulatedEvent.recycle();
+    }
+
     public void releaseVelocityTracker() {
         if (mVelocityTracker != null) {
             mVelocityTracker.recycle();
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
new file mode 100644 (file)
index 0000000..1a99cc8
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2017 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.ClipDescription;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.DragEvent;
+import android.view.View;
+
+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.compat.PinItemRequestCompat;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.PendingItemPreviewProvider;
+
+import java.util.UUID;
+
+/**
+ * {@link DragSource} for handling drop from from a different window. This object is initialized
+ * in the source window and is passed on to the Launcher activity as an Intent extra.
+ */
+public class PinItemDragListener implements Parcelable, View.OnDragListener, DragSource {
+
+    private static final String TAG = "PinItemDragListener";
+
+    private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/";
+    public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener";
+
+    private final PinItemRequestCompat mRequest;
+
+    // Position of preview relative to the touch location
+    private final Rect mPreviewRect;
+
+    // Randomly generated id used to verify the drag event.
+    private final String mId;
+
+    private Launcher mLauncher;
+    private DragController mDragController;
+    private long mDragStartTime;
+
+    public PinItemDragListener(PinItemRequestCompat request, Rect previewRect) {
+        mRequest = request;
+        mPreviewRect = previewRect;
+        mId = UUID.randomUUID().toString();
+    }
+
+    private PinItemDragListener(Parcel parcel) {
+        mRequest = PinItemRequestCompat.CREATOR.createFromParcel(parcel);
+        mPreviewRect = Rect.CREATOR.createFromParcel(parcel);
+        mId = parcel.readString();
+    }
+
+    public String getMimeType() {
+        return MIME_TYPE_PREFIX + mId;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int i) {
+        mRequest.writeToParcel(parcel, i);
+        mPreviewRect.writeToParcel(parcel, i);
+        parcel.writeString(mId);
+    }
+
+    public void setLauncher(Launcher launcher) {
+        mLauncher = launcher;
+        mDragController = launcher.getDragController();
+    }
+
+    @Override
+    public boolean onDrag(View view, DragEvent event) {
+        if (mLauncher == null || mDragController == null) {
+            postCleanup();
+            return false;
+        }
+        if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
+            if (onDragStart(event)) {
+                return true;
+            } else {
+                postCleanup();
+                return false;
+            }
+        }
+        return mDragController.onDragEvent(mDragStartTime, event);
+    }
+
+    private boolean onDragStart(DragEvent event) {
+        if (!mRequest.isValid()) {
+            return false;
+        }
+        ClipDescription desc =  event.getClipDescription();
+        if (desc == null || !desc.hasMimeType(getMimeType())) {
+            Log.e(TAG, "Someone started a dragAndDrop before us.");
+            return false;
+        }
+
+        if (mLauncher.isWorkspaceLocked()) {
+            // TODO: implement wait
+            return false;
+        }
+
+        final PendingAddItemInfo item;
+        final Bitmap preview;
+
+        Point dragShift = new Point(mPreviewRect.left, mPreviewRect.top);
+        if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) {
+            item = new PendingAddShortcutInfo(
+                    new PinShortcutRequestActivityInfo(mRequest, mLauncher));
+
+            ShortcutInfoCompat compat = new ShortcutInfoCompat(mRequest.getShortcutInfo());
+            Bitmap icon = LauncherIcons.createShortcutIcon(compat, mLauncher, false /* badged */);
+
+            // Create a preview same as the workspace cell size and draw the icon at the
+            // appropriate position.
+            int[] size = mLauncher.getWorkspace().estimateItemSize(item, true, false);
+            preview = Bitmap.createBitmap(size[0], size[1], Bitmap.Config.ARGB_8888);
+            Canvas c = new Canvas(preview);
+            DeviceProfile dp = mLauncher.getDeviceProfile();
+            c.drawBitmap(icon, (size[0] - icon.getWidth()) / 2,
+                    (size[1] - icon.getHeight() - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2,
+                    new Paint(Paint.FILTER_BITMAP_FLAG));
+        } else {
+            PendingAddWidgetInfo info = new PendingAddWidgetInfo(
+                    LauncherAppWidgetProviderInfo.fromProviderInfo(
+                            mLauncher, mRequest.getAppWidgetProviderInfo(mLauncher)));
+            int[] size = mLauncher.getWorkspace().estimateItemSize(info, true, false);
+
+            float minScale = 1.25f;
+            int maxWidth = Math.min((int) (mPreviewRect.width() * minScale), size[0]);
+            int[] previewSizeBeforeScale = new int[1];
+            preview = LauncherAppState.getInstance(mLauncher).getWidgetCache()
+                    .generateWidgetPreview(mLauncher, info.info, maxWidth, null,
+                            previewSizeBeforeScale);
+
+            dragShift.offset(
+                    (mPreviewRect.width() - preview.getWidth()) / 2,
+                    (mPreviewRect.height() - preview.getHeight()) / 2);
+            item = info;
+        }
+
+        PendingItemPreviewProvider previewProvider =
+                new PendingItemPreviewProvider(new View(mLauncher), item, preview);
+
+        // Since we are not going through the workspace for starting the drag, set drag related
+        // information on the workspace before starting the drag.
+        mLauncher.getWorkspace().prepareDragWithProvider(previewProvider);
+
+        Point downPos = new Point((int) event.getX(), (int) event.getY());
+        DragOptions options = new DragOptions();
+        options.systemDndStartPoint = downPos;
+
+        int x = downPos.x + dragShift.x;
+        int y = downPos.y + dragShift.y;
+        mDragController.startDrag(
+                preview, x, y, this, item, null, null, 1f, options);
+        mDragStartTime = SystemClock.uptimeMillis();
+        return true;
+    }
+
+    @Override
+    public boolean supportsAppInfoDropTarget() {
+        return false;
+    }
+
+    @Override
+    public boolean supportsDeleteDropTarget() {
+        return false;
+    }
+
+    @Override
+    public float getIntrinsicIconScaleFactor() {
+        return 1f;
+    }
+
+    @Override
+    public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
+            boolean success) {
+        if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
+                !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
+            // Exit spring loaded mode if we have not successfully dropped or have not handled the
+            // drop in Workspace
+            mLauncher.exitSpringLoadedDragModeDelayed(true,
+                    Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+        }
+        postCleanup();
+    }
+
+    @Override
+    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
+            LauncherLogProto.Target targetParent) {
+        // TODO: We should probably log something
+    }
+
+    private void postCleanup() {
+        new Handler(Looper.getMainLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                removeListener();
+            }
+        });
+    }
+
+    public void removeListener() {
+        if (mLauncher != null) {
+            mLauncher.getDragLayer().setOnDragListener(null);
+        }
+    }
+
+    public static final Parcelable.Creator<PinItemDragListener> CREATOR =
+            new Parcelable.Creator<PinItemDragListener>() {
+                public PinItemDragListener createFromParcel(Parcel source) {
+                    return new PinItemDragListener(source);
+                }
+
+                public PinItemDragListener[] newArray(int size) {
+                    return new PinItemDragListener[size];
+                }
+            };
+}
index d1f878a..2121b43 100644 (file)
@@ -26,7 +26,9 @@ import android.graphics.drawable.Drawable;
 import android.os.Build;
 
 import com.android.launcher3.IconCache;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.compat.PinItemRequestCompat;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo;
 
 /**
@@ -40,12 +42,15 @@ class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo {
     // actual existing class.
     private static final String DUMMY_COMPONENT_CLASS = "pinned-shortcut";
 
+    private final PinItemRequestCompat mRequest;
     private final ShortcutInfo mInfo;
     private final Context mContext;
 
-    public PinShortcutRequestActivityInfo(ShortcutInfo info, Context context) {
-        super(new ComponentName(info.getPackage(), DUMMY_COMPONENT_CLASS), info.getUserHandle());
-        mInfo = info;
+    public PinShortcutRequestActivityInfo(PinItemRequestCompat request, Context context) {
+        super(new ComponentName(request.getShortcutInfo().getPackage(), DUMMY_COMPONENT_CLASS),
+                request.getShortcutInfo().getUserHandle());
+        mRequest = request;
+        mInfo = request.getShortcutInfo();
         mContext = context;
     }
 
@@ -61,8 +66,9 @@ class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo {
     }
 
     @Override
-    public boolean startConfigActivity(Activity activity, int requestCode) {
-        throw new RuntimeException("Not supported");
+    public boolean startConfigActivity(Launcher activity, int requestCode) {
+        activity.onActivityResult(requestCode, Activity.RESULT_OK, mRequest.toIntent());
+        return true;
     }
 
     @Override
index 87247f4..455ec4e 100644 (file)
@@ -144,12 +144,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
         }
     }
 
-    public int[] getPreviewSize() {
-        int[] maxSize = new int[2];
-
-        maxSize[0] = mPresetPreviewSize;
-        maxSize[1] = mPresetPreviewSize;
-        return maxSize;
+    public WidgetImageView getWidgetView() {
+        return mWidgetImage;
     }
 
     public void applyPreview(Bitmap bitmap) {
@@ -166,12 +162,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
         if (mActiveRequest != null) {
             return;
         }
-        int[] size = getPreviewSize();
-        if (DEBUG) {
-            Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):",
-                    getTagToString(), size[0], size[1]));
-        }
-        mActiveRequest = mWidgetPreviewLoader.getPreview(mItem, size[0], size[1], this);
+        mActiveRequest = mWidgetPreviewLoader.getPreview(
+                mItem, mPresetPreviewSize, mPresetPreviewSize, this);
     }
 
     @Override